crosspad-mcp-server 9.1.0 → 9.2.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 (43) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/config.d.ts +7 -0
  3. package/dist/config.js +15 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/index.js +129 -29
  6. package/dist/index.js.map +1 -1
  7. package/dist/tools/stm-build.d.ts +23 -0
  8. package/dist/tools/stm-build.js +78 -0
  9. package/dist/tools/stm-build.js.map +1 -0
  10. package/dist/tools/stm-flash.d.ts +36 -0
  11. package/dist/tools/stm-flash.js +112 -0
  12. package/dist/tools/stm-flash.js.map +1 -0
  13. package/dist/tools/trace-session.d.ts +12 -0
  14. package/dist/tools/trace-session.js +69 -0
  15. package/dist/tools/trace-session.js.map +1 -1
  16. package/dist/tools/trace-webui.d.ts +14 -7
  17. package/dist/tools/trace-webui.js +77 -33
  18. package/dist/tools/trace-webui.js.map +1 -1
  19. package/dist/tools/trace-write.d.ts +9 -0
  20. package/dist/tools/trace-write.js +32 -0
  21. package/dist/tools/trace-write.js.map +1 -0
  22. package/dist/utils/userConfig.d.ts +9 -0
  23. package/dist/utils/userConfig.js.map +1 -1
  24. package/package.json +2 -1
  25. package/skills/swd-tracer/SKILL.md +48 -0
  26. package/tracer/PROTOCOL.md +69 -0
  27. package/tracer/swd_tracer.py +460 -15
  28. package/tracer/ui/config.js +40 -0
  29. package/tracer/ui/connection.js +170 -0
  30. package/tracer/ui/index.html +50 -798
  31. package/tracer/ui/main.js +30 -0
  32. package/tracer/ui/plot.js +337 -0
  33. package/tracer/ui/signals.js +39 -0
  34. package/tracer/ui/state.js +57 -0
  35. package/tracer/ui/stats.js +52 -0
  36. package/tracer/ui/style.css +97 -0
  37. package/tracer/ui/symbols.js +80 -0
  38. package/tracer/ui/toolbar.js +95 -0
  39. package/tracer/ui/util.js +62 -0
  40. package/tracer/ui/watchlist.js +179 -0
  41. package/vscode-extension/README.md +32 -0
  42. package/vscode-extension/extension.js +56 -0
  43. package/vscode-extension/package.json +14 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "crosspad",
3
3
  "description": "CrossPad development toolkit: the crosspad-mcp server plus two Claude Code skills — `crosspad` (onboarding/help: ecosystem map, install & config, per-role guides, tool cheat-sheet, FAQ — use this first) and `swd-tracer` (real-time SWD variable tracing for CrossPad r20 / STM32G0B1 firmware over ST-Link).",
4
- "version": "9.1.0",
4
+ "version": "9.1.1",
5
5
  "author": {
6
6
  "name": "Mateusz Czarnecki"
7
7
  },
package/dist/config.d.ts CHANGED
@@ -5,6 +5,13 @@ export declare const CROSSPAD_IDF_ROOT: string;
5
5
  export declare const CROSSPAD_STM_ROOT: string;
6
6
  export declare const STM_ELF_DEFAULT: string;
7
7
  export declare const TRACE_DIR_DEFAULT: string;
8
+ export declare const STM_PROJECT_NAME = "CrossPad_STM32_r20";
9
+ export declare const STM_FLASH_ADDR = "0x08000000";
10
+ export type StmPreset = "Debug" | "Release";
11
+ /** Build directory for a preset: <repo>/build/<preset>. */
12
+ export declare function stmBuildDir(preset: StmPreset): string;
13
+ /** Artifact path for a preset, e.g. build/Debug/CrossPad_STM32_r20.elf. */
14
+ export declare function stmArtifact(preset: StmPreset, ext: "elf" | "bin" | "hex"): string;
8
15
  export declare const IDF_PATH: string;
9
16
  export declare const VCVARSALL: string;
10
17
  export declare const VCPKG_TOOLCHAIN: string;
package/dist/config.js CHANGED
@@ -23,6 +23,21 @@ export const CROSSPAD_STM_ROOT = process.env.CROSSPAD_STM_ROOT || path.join(GIT_
23
23
  // Trace defaults (overridable later via user config at call sites).
24
24
  export const STM_ELF_DEFAULT = path.join(CROSSPAD_STM_ROOT, "build", "Debug", "CrossPad_STM32_r20.elf");
25
25
  export const TRACE_DIR_DEFAULT = path.join(CROSSPAD_STM_ROOT, "traces");
26
+ // ─── STM32 build / flash ─────────────────────────────────────────────────
27
+ // CMake project name (artifact basename) and the CMakePresets.json preset
28
+ // names. binaryDir is build/<preset> per the preset's `binaryDir` template.
29
+ export const STM_PROJECT_NAME = "CrossPad_STM32_r20";
30
+ // Flash base address — STM32G0 internal flash origin (matches the *.ld linker
31
+ // script ORIGIN and the STM32_Programmer_CLI invocations in CLAUDE.md).
32
+ export const STM_FLASH_ADDR = "0x08000000";
33
+ /** Build directory for a preset: <repo>/build/<preset>. */
34
+ export function stmBuildDir(preset) {
35
+ return path.join(CROSSPAD_STM_ROOT, "build", preset);
36
+ }
37
+ /** Artifact path for a preset, e.g. build/Debug/CrossPad_STM32_r20.elf. */
38
+ export function stmArtifact(preset, ext) {
39
+ return path.join(stmBuildDir(preset), `${STM_PROJECT_NAME}.${ext}`);
40
+ }
26
41
  // ═══════════════════════════════════════════════════════════════════════
27
42
  // ESP-IDF SDK PATH — fallback chain
28
43
  // ═══════════════════════════════════════════════════════════════════════
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -11,6 +11,8 @@ import { BIN_EXE as _BIN_EXE } from "./config.js";
11
11
  import { crosspadLog } from "./tools/log.js";
12
12
  import { crosspadIdfBuild } from "./tools/idf-build.js";
13
13
  import { crosspadIdfFlash, crosspadIdfOta } from "./tools/idf-flash.js";
14
+ import { crosspadStmBuild } from "./tools/stm-build.js";
15
+ import { crosspadStmFlash } from "./tools/stm-flash.js";
14
16
  import { crosspadIdfMonitor } from "./tools/idf-monitor.js";
15
17
  import { listDevices } from "./utils/device.js";
16
18
  import { crosspadTest } from "./tools/test.js";
@@ -26,10 +28,10 @@ import { crosspadSettingsGet, crosspadSettingsSet } from "./tools/settings.js";
26
28
  import { crosspadMidiSend } from "./tools/midi.js";
27
29
  import { crosspadAppList, crosspadAppInstall, crosspadAppRemove, crosspadAppUpdate, crosspadAppSync, } from "./tools/app-manager.js";
28
30
  import { runDoctor, realProbe } from "./tools/trace-doctor.js";
29
- import { setConfigValue } from "./utils/userConfig.js";
31
+ import { setConfigValue, resolveConfigValue } from "./utils/userConfig.js";
30
32
  import { listSymbols } from "./tools/trace-symbols.js";
31
33
  import { getDeviceState } from "./tools/trace-device.js";
32
- import { TraceSession, getActiveSession, setActiveSession } from "./tools/trace-session.js";
34
+ import { TraceSession, getActiveSession, setActiveSession, traceWrite, traceCall } from "./tools/trace-session.js";
33
35
  import { getDashboard, openInBrowser, buildUiUrl } from "./tools/trace-webui.js";
34
36
  import { writeCsv } from "./tools/trace-export.js";
35
37
  // Server instructions — MCP clients prepend these to the LLM system prompt.
@@ -42,14 +44,15 @@ You have access to the CrossPad MCP server, which exposes purpose-built tools fo
42
44
 
43
45
  NEW TO A CROSSPAD REPO OR SETTING UP? Use the \`crosspad\` skill first — it maps the ecosystem (repos, MCP tools, roles), walks install/config, and routes to per-role guides + an FAQ. Run \`bash scripts/doctor.sh\` from that skill to check your environment.
44
46
 
45
- TOOL TAGS — a tool description starting with a bracket tag has a hardware/platform precondition: \`[PC sim]\` needs the host simulator built & running (crosspad_build platform=pc crosspad_run); \`[ESP HW]\` needs a connected ESP32-S3 device; \`[STM HW]\` needs an ST-Link + STM32 board. Untagged tools (code search, repo/git, apps registry) need no hardware.
47
+ TOOL TAGS — a tool description starting with a bracket tag has a platform/hardware precondition. Atomic tokens: \`[PC]\` = the crosspad-pc repo + host toolchain (build/test/inspect no running sim); \`[PC sim]\` = a RUNNING simulator instance (launch it with crosspad_run first); \`[ESP HW]\` = a connected ESP32-S3 device; \`[STM HW]\` = an ST-Link + STM32 board. Tokens combine: \`+\` = both contexts at once (e.g. \`[PC + ESP]\`); \`|\` = either, depending on a param (e.g. \`[PC | ESP HW]\`). Untagged tools (code search, repo/git, apps registry) need no hardware.
46
48
 
47
49
  WHEN TO USE THESE TOOLS — in any conversation that touches a CrossPad repo, prefer the crosspad_* tools over raw shell equivalents:
48
50
 
49
51
  - Inspecting code → crosspad_search_symbols (NOT \`grep -r\`); crosspad_list_interfaces; crosspad_interface_implementations.
50
52
  - Repo state → crosspad_repo_status (NOT \`git status\` across N repos); crosspad_repo_diff for submodule drift.
51
53
  - Building PC sim → crosspad_check platform=pc → crosspad_build platform=pc (NOT raw cmake/ninja). Then crosspad_run; crosspad_kill when done.
52
- - Building firmware→ crosspad_build platform=idf (NOT raw \`idf.py build\`); crosspad_flash transport=uart|ota.
54
+ - Building ESP fw → crosspad_build platform=idf (NOT raw \`idf.py build\`); crosspad_flash target=esp transport=uart|ota.
55
+ - Building STM fw → crosspad_build platform=stm (NOT raw cmake/STM32_Programmer_CLI for CrossPad_STM32_r20); crosspad_flash target=stm method=swd|dfu.
53
56
  - Tests → crosspad_test_run (NOT raw catch2 binary).
54
57
  - Sim interaction → crosspad_screenshot, crosspad_input, crosspad_midi, crosspad_stats, crosspad_settings_get/set.
55
58
  - Apps (registry) → crosspad_apps_list / install / remove / update / sync (NOT manual submodule git ops).
@@ -212,8 +215,12 @@ const O_BuildCheck = {
212
215
  };
213
216
  const O_Flash = {
214
217
  success: z.boolean(),
215
- method: z.enum(["uart", "ota"]),
216
- port: z.string(),
218
+ method: z.enum(["uart", "ota", "swd", "dfu"]),
219
+ // ESP transports report the serial port; STM methods report the flasher
220
+ // binary + firmware path instead. Both kept optional so each path validates.
221
+ port: z.string().optional(),
222
+ programmer: z.string().optional(),
223
+ firmware_path: z.string().optional(),
217
224
  duration_seconds: z.number(),
218
225
  output_tail: z.array(z.string()),
219
226
  ...ErrorField,
@@ -386,26 +393,27 @@ const O_AppAction = {
386
393
  // BUILD — unified across platforms (pc, idf)
387
394
  // `platform` arg disambiguates. Modes are validated per-platform at runtime.
388
395
  // ═══════════════════════════════════════════════════════════════════════
389
- const BuildPlatform = z.enum(["pc", "idf"]).describe("Target platform: 'pc' = host simulator, 'idf' = ESP32-S3 firmware.");
396
+ const BuildPlatform = z.enum(["pc", "idf", "stm"]).describe("Target platform: 'pc' = host simulator, 'idf' = ESP32-S3 firmware, 'stm' = STM32G0 firmware (CrossPad r20).");
390
397
  const PlatformPcOnly = z.enum(["pc"]).default("pc").describe("Platform — currently only 'pc' is supported here.");
391
398
  server.registerTool("crosspad_build", {
392
- description: "[PC + ESP] Build CrossPad for the given platform.\n" +
399
+ description: "[PC | ESP | STM HW] Build CrossPad for the given platform.\n" +
393
400
  " • platform='pc' → CMake + Ninja host simulator. PREFER THIS over `cmake --build build` (picks right MSVC env on Windows, parses errors/warnings, streams progress).\n" +
394
401
  " • platform='idf' → idf.py build for ESP32-S3 firmware. PREFER THIS over raw `idf.py build` (sources IDF env, auto-fullcleans when new apps detected, parses errors/warnings).\n" +
402
+ " • platform='stm' → CMake + Ninja + arm-none-eabi for STM32G0 firmware (CrossPad r20). Uses CMakePresets (Debug/Release); output is build/<preset>/CrossPad_STM32_r20.elf.\n" +
395
403
  "Mode×platform compatibility:\n" +
396
- " • incremental → both (default)\n" +
397
- " • clean → both (wipes build dir, then builds)\n" +
398
- " • reconfigure → PC only (re-runs cmake without wiping cache)\n" +
404
+ " • incremental → all (default)\n" +
405
+ " • clean → all (wipes build dir, then builds)\n" +
406
+ " • reconfigure → PC & STM (re-runs cmake without wiping cache)\n" +
399
407
  " • fullclean → IDF only (runs idf.py fullclean, then builds)",
400
408
  inputSchema: {
401
409
  platform: BuildPlatform,
402
410
  mode: z.enum(["incremental", "clean", "fullclean", "reconfigure"])
403
411
  .default("incremental")
404
- .describe("Build mode. Compatibility: incremental & clean = both platforms; reconfigure = PC only; fullclean = IDF only. " +
405
- "Pick incremental for normal iteration; clean if you suspect stale artifacts; fullclean (IDF) after adding new apps; reconfigure (PC) after editing CMakeLists."),
412
+ .describe("Build mode. Compatibility: incremental & clean = all platforms; reconfigure = PC & STM; fullclean = IDF only. " +
413
+ "Pick incremental for normal iteration; clean if you suspect stale artifacts; fullclean (IDF) after adding new apps; reconfigure (PC/STM) after editing CMakeLists/presets."),
406
414
  build_type: z.enum(["Debug", "Release", "RelWithDebInfo"])
407
415
  .default("Debug")
408
- .describe("CMake build type — PC ONLY (ignored for IDF; ESP32 build type comes from sdkconfig). Only honored on mode=clean|reconfigure (incremental keeps existing cache)."),
416
+ .describe("CMake build type — PC & STM (ignored for IDF; ESP32 build type comes from sdkconfig). STM maps to the Debug/Release preset (RelWithDebInfo→Release). Only honored on mode=clean|reconfigure (incremental keeps existing cache)."),
409
417
  },
410
418
  outputSchema: O_Build,
411
419
  annotations: ANN_DESTRUCTIVE,
@@ -416,15 +424,21 @@ server.registerTool("crosspad_build", {
416
424
  const onLine = makeProgressLogger("build-pc", extra);
417
425
  return jsonResponse(await crosspadBuild(mode, onLine, build_type, extra.signal));
418
426
  }
427
+ if (platform === "stm") {
428
+ if (mode === "fullclean")
429
+ return err("mode='fullclean' is IDF-only. STM supports: incremental, clean, reconfigure.");
430
+ const onLine = makeProgressLogger("build-stm", extra);
431
+ return jsonResponse(await crosspadStmBuild(mode, onLine, build_type, extra.signal));
432
+ }
419
433
  // idf
420
434
  if (mode === "reconfigure")
421
- return err("mode='reconfigure' is PC-only. IDF supports: incremental, clean, fullclean.");
435
+ return err("mode='reconfigure' is PC/STM-only. IDF supports: incremental, clean, fullclean.");
422
436
  const idfMode = mode === "incremental" ? "build" : mode;
423
437
  const onLine = makeProgressLogger("build-idf", extra);
424
438
  return jsonResponse(await crosspadIdfBuild(idfMode, onLine, extra.signal));
425
439
  });
426
440
  server.registerTool("crosspad_run", {
427
- description: "[PC sim] Launch the built simulator binary in the background. Returns pid + exe_path. Refuses to spawn a duplicate if one is already responding on the TCP control port (use force=true to override). Fails if binary not built — call crosspad_build first. Currently PC-only (IDF firmware doesn't run on the host).",
441
+ description: "[PC] Launch the built simulator binary in the background. Returns pid + exe_path. Refuses to spawn a duplicate if one is already responding on the TCP control port (use force=true to override). Fails if binary not built — call crosspad_build first. Currently PC-only (IDF firmware doesn't run on the host).",
428
442
  inputSchema: {
429
443
  platform: PlatformPcOnly,
430
444
  force: z.boolean().default(false)
@@ -454,7 +468,7 @@ server.registerTool("crosspad_kill", {
454
468
  annotations: ANN_DESTRUCTIVE,
455
469
  }, async () => jsonResponse(await crosspadKill()));
456
470
  server.registerTool("crosspad_check", {
457
- description: "[PC sim] Health check for a build — detects stale exe, new sources missing from build system, dirty submodules. Use before crosspad_build to decide if rebuild needed. Currently PC-only.",
471
+ description: "[PC] Health check for a build — detects stale exe, new sources missing from build system, dirty submodules. Use before crosspad_build to decide if rebuild needed. Currently PC-only.",
458
472
  inputSchema: {
459
473
  platform: PlatformPcOnly,
460
474
  },
@@ -465,16 +479,45 @@ server.registerTool("crosspad_check", {
465
479
  // FLASH — unified UART/OTA into one tool with `transport` axis
466
480
  // ═══════════════════════════════════════════════════════════════════════
467
481
  server.registerTool("crosspad_flash", {
468
- description: "[ESP HW] Flash ESP firmware to a connected CrossPad device. transport='uart' uses idf.py flash (device must be in bootloader mode). transport='ota' uses platform-idf/tools/ota_flash.py over USB CDC (no bootloader mode required). Requires prior crosspad_build platform=idf. Works on both CrossPad generations: rev <2.0 (ESP native USB) and rev 2.0 (port is the STM32 CDC bridge — STM emulates the esptool DTR/RTS auto-reset and forwards the flash to the ESP over LPUART2; rev-2.0 STM must be in passthrough mode, i.e. NOT booted with pad-4 held).",
482
+ description: "[ESP HW | STM HW] Flash firmware to a connected CrossPad device.\n" +
483
+ "target='esp' (default) → ESP32-S3 firmware (requires prior crosspad_build platform=idf):\n" +
484
+ " • transport='uart' uses idf.py flash (device must be in bootloader mode).\n" +
485
+ " • transport='ota' uses platform-idf/tools/ota_flash.py over USB CDC (no bootloader mode required).\n" +
486
+ " Works on both CrossPad generations: rev <2.0 (ESP native USB) and rev 2.0 (port is the STM32 CDC bridge — STM emulates the esptool DTR/RTS auto-reset and forwards the flash to the ESP over LPUART2; rev-2.0 STM must be in passthrough mode, i.e. NOT booted with pad-4 held).\n" +
487
+ "target='stm' → STM32G0 firmware via STM32_Programmer_CLI (requires prior crosspad_build platform=stm):\n" +
488
+ " • method='swd' flashes over ST-Link (SWD).\n" +
489
+ " • method='dfu' flashes the USB DFU bootloader (board in ST system memory — hold pad 1 at boot or trigger boot_request_dfu).\n" +
490
+ " Flasher resolved from config (stm_programmer_cli) → $STM32_PROG → PATH.",
469
491
  inputSchema: {
470
- transport: z.enum(["uart", "ota"]).describe("'uart' = bootloader-mode flash via idf.py; 'ota' = USB-CDC OTA flash via ota_flash.py."),
492
+ target: z.enum(["esp", "stm"]).default("esp").describe("'esp' = ESP32-S3 (transport uart/ota); 'stm' = STM32G0 firmware via STM32_Programmer_CLI (method swd/dfu)."),
493
+ transport: z.enum(["uart", "ota"]).optional().describe("ESP only. 'uart' = bootloader-mode flash via idf.py; 'ota' = USB-CDC OTA flash via ota_flash.py."),
494
+ method: z.enum(["swd", "dfu"]).optional().describe("STM only. 'swd' = ST-Link/SWD; 'dfu' = USB DFU system bootloader."),
471
495
  port: Port.optional(),
496
+ build_type: z.enum(["Debug", "Release", "RelWithDebInfo"]).optional()
497
+ .describe("STM only. Selects the build/<preset> dir for the default firmware binary. Defaults to Debug."),
472
498
  firmware_path: z.string().optional()
473
- .describe("Custom firmware binary path (OTA only). Defaults to <idf-root>/build/CrossPad.bin."),
499
+ .describe("Custom firmware binary path. ESP: OTA only, defaults to <idf-root>/build/CrossPad.bin. STM: defaults to <stm-root>/build/<preset>/CrossPad_STM32_r20.bin."),
474
500
  },
475
501
  outputSchema: O_Flash,
476
502
  annotations: ANN_DESTRUCTIVE,
477
- }, async ({ transport, port, firmware_path }, extra) => {
503
+ }, async ({ target, transport, method, port, build_type, firmware_path }, extra) => {
504
+ if (target === "stm") {
505
+ if (!method)
506
+ return err("target='stm' requires 'method' (swd or dfu).");
507
+ if (transport)
508
+ return err("Field 'transport' is ESP-only. For STM use 'method' (swd|dfu).");
509
+ if (port)
510
+ return err("Field 'port' is ESP-only — STM flash addresses the ST-Link/DFU device, not a serial port.");
511
+ const onLine = makeProgressLogger(`flash-stm-${method}`, extra);
512
+ return jsonResponse(await crosspadStmFlash(method, build_type ?? "Debug", firmware_path, onLine, extra.signal));
513
+ }
514
+ // esp
515
+ if (method)
516
+ return err("Field 'method' is STM-only. For ESP use 'transport' (uart|ota).");
517
+ if (build_type)
518
+ return err("Field 'build_type' is STM-only (ESP build type comes from sdkconfig).");
519
+ if (!transport)
520
+ return err("target='esp' requires 'transport' (uart or ota).");
478
521
  if (transport === "uart") {
479
522
  if (firmware_path)
480
523
  return err("Field 'firmware_path' is OTA-only — UART flash always uses the active build dir.");
@@ -486,7 +529,7 @@ server.registerTool("crosspad_flash", {
486
529
  return jsonResponse(await crosspadIdfOta(port, firmware_path, onLine, extra.signal));
487
530
  });
488
531
  server.registerTool("crosspad_log", {
489
- description: "[PC sim | ESP HW] Capture logs (consolidated; replaces crosspad_log_pc and crosspad_log_idf in v6).\n" +
532
+ description: "[PC | ESP HW] Capture logs (consolidated; replaces crosspad_log_pc and crosspad_log_idf in v6).\n" +
490
533
  " • target='pc' → spawn the built sim binary, capture stdout/stderr, then kill it. " +
491
534
  "Fields used: timeout_seconds (default 5), max_lines (default 200). `port` and `filter` MUST be omitted.\n" +
492
535
  " • target='idf' → read serial from a connected ESP32-S3 via pyserial (no TTY needed). " +
@@ -537,6 +580,7 @@ export function setTraceBrowserOpener(fn) { traceBrowserOpener = fn; }
537
580
  const TraceAction = z.enum([
538
581
  "doctor", "config_set", "symbols", "start", "stop",
539
582
  "add", "remove", "status", "read", "save", "device_state", "ui",
583
+ "write", "call",
540
584
  ]);
541
585
  server.registerTool("crosspad_trace", {
542
586
  description: "[STM HW] Real-time SWD tracer for the STM32G0B1 firmware (ST-Link). Non-halting RAM polling of firmware variables resolved from the Debug ELF (like ST-Studio/CubeMonitor). Pick an `action`:\n" +
@@ -555,28 +599,35 @@ server.registerTool("crosspad_trace", {
555
599
  inputSchema: {
556
600
  action: TraceAction.describe("Required params per action — doctor/stop/status/device_state/ui: (none); " +
557
601
  "config_set: key,value; symbols: query?; start: signals[],rate_hz?; " +
558
- "add/remove: signals[]; read: window_from?,window_to?,max_points?; save: format?."),
559
- signals: z.array(z.string()).optional().describe("start: variable names from `symbols` (e.g. ['s_vbat_mv','s_inputs[0]'])."),
602
+ "add/remove: signals[]; read: window_from?,window_to?,max_points?; save: format?; " +
603
+ "write: writes[]; call: func,args?,confirm,ret_type?,timeout?."),
604
+ signals: z.array(z.string()).optional().describe("start: variable names from `symbols` (e.g. ['s_vbat_mv','s_inputs[0]']). Also accepts raw @address specs that bypass DWARF — '@0x40021000' (u32), '@0x40021000:u16' (u8|u16|u32|i8|i16|i32|f32), '@0x20000000:u8[16]' (16-element block) — for peripheral registers / arbitrary RAM."),
560
605
  rate_hz: z.number().int().min(0).max(2000).optional().describe("start: target sample rate (0 = as fast as the probe allows). Actual Fs is reported."),
561
606
  swo: z.array(z.string()).optional().describe("start (EXPERIMENTAL): map ITM stimulus ports to signal names, e.g. ['0:phase','1:isr_us']. Requires firmware that emits ITM on the SWO pin (NOT present in current CrossPad firmware — UNTESTED against real ITM). Omit for plain RAM polling. Fails soft: if SWV init fails, polling continues normally."),
562
607
  query: z.string().optional().describe("symbols: case-insensitive substring filter."),
563
- key: z.string().optional().describe("config_set: one of stm_elf_path|pyocd_python|probe_serial|trace_dir."),
608
+ key: z.string().optional().describe("config_set: one of stm_elf_path|pyocd_python|probe_serial|trace_dir|ui_open|stm_programmer_cli. stm_programmer_cli = path to STM32_Programmer_CLI for crosspad_flash target=stm. ui_open ∈ vscode(default: reply with the link → user clicks → opens in the VS Code Simple Browser; system-browser fallback after 30s if unopened)|browser(open system browser immediately)|none(never auto-open)."),
564
609
  value: z.string().optional().describe("config_set: the value to persist."),
565
610
  window_from: z.number().optional().describe("read: start time (s) of the window."),
566
611
  window_to: z.number().optional().describe("read: end time (s) of the window."),
567
612
  max_points: z.number().int().min(1).max(5000).optional().describe("read: max points per signal (default 200)."),
568
613
  format: z.enum(["csv"]).optional().describe("save: export format (csv)."),
614
+ writes: z.array(z.string()).optional().describe("write: list of 'target=value' specs. target = @0xADDR[:type] (u8|u16|u32|i8|i16|i32|f32, default u32) or a DWARF symbol; value = hex 0x.. or decimal (float for f32). e.g. ['@0x50000414:u16=0xFFFF','s_vbat_mv=4200']. Allowlist: SRAM/peripheral/PPB only — Code/flash region is blocked."),
615
+ func: z.string().optional().describe("call: firmware function symbol to invoke (AAPCS)."),
616
+ args: z.array(z.number().int()).max(4).optional().describe("call: up to 4 integer args → r0-r3."),
617
+ confirm: z.boolean().optional().describe("call: must be true — acknowledges the core is halted for the call."),
618
+ ret_type: z.enum(["u32", "i32", "u16", "i16", "u8", "i8", "f32"]).optional().describe("call: decode r0 as this type (default u32; raw r0 always returned)."),
619
+ timeout: z.number().min(0.1).max(30).optional().describe("call: max seconds to wait for the function to return (default 2)."),
569
620
  },
570
621
  outputSchema: O_Trace,
571
622
  annotations: ANN_SIDE_EFFECT,
572
- }, async ({ action, signals, rate_hz, swo, query, key, value, window_from, window_to, max_points, format }, extra) => {
623
+ }, async ({ action, signals, rate_hz, swo, query, key, value, window_from, window_to, max_points, format, writes, func, args, confirm, ret_type, timeout }, extra) => {
573
624
  switch (action) {
574
625
  case "doctor": {
575
626
  const r = await runDoctor(realProbe());
576
627
  return ok({ action, ok: r.ok, issues: r.issues, device_state: r.probe ? "connected" : "no_probe" });
577
628
  }
578
629
  case "config_set": {
579
- const allowed = ["stm_elf_path", "pyocd_python", "probe_serial", "trace_dir"];
630
+ const allowed = ["stm_elf_path", "pyocd_python", "probe_serial", "trace_dir", "ui_open", "stm_programmer_cli"];
580
631
  if (!key || !allowed.includes(key))
581
632
  return err(`config_set requires key in ${allowed.join("|")}`);
582
633
  if (value === undefined)
@@ -615,9 +666,32 @@ server.registerTool("crosspad_trace", {
615
666
  let uiUrl;
616
667
  try {
617
668
  uiUrl = await dashboard.ensureStarted();
618
- if (!dashboard.hasClients())
619
- traceBrowserOpener(uiUrl);
620
669
  dashboard.bind(sess);
670
+ // §12.6 open behavior, by ui_open config (default "vscode"):
671
+ // vscode → DON'T pop anything now. The agent always replies with the
672
+ // dashboard link; the user clicks it → it opens in the VS Code
673
+ // Simple Browser via their workbench.externalUriOpeners. If
674
+ // nobody opens it within the fallback window, fall back to the
675
+ // system browser so a first-timer is never left staring at nothing.
676
+ // browser → open the system browser immediately.
677
+ // none → never auto-open (rely on an already-open persistent tab).
678
+ if (uiUrl && !dashboard.hasClients()) {
679
+ const mode = resolveConfigValue("ui_open", "CROSSPAD_TRACE_UI_OPEN", process.env.CROSSPAD_TRACE_UI_OPEN, "vscode");
680
+ if (mode === "browser") {
681
+ traceBrowserOpener(uiUrl);
682
+ }
683
+ else if (mode === "vscode") {
684
+ const ms = Number(process.env.CROSSPAD_TRACE_OPEN_FALLBACK_MS) || 30000;
685
+ const url = uiUrl;
686
+ setTimeout(() => {
687
+ // Only fall back if THIS trace is still the active one and nobody
688
+ // ever opened the in-editor link.
689
+ if (getActiveSession() === sess && sess.isRunning() && !getDashboard().hasClients()) {
690
+ traceBrowserOpener(url);
691
+ }
692
+ }, ms);
693
+ }
694
+ }
621
695
  }
622
696
  catch { /* UI optional — never block a trace on the dashboard */ }
623
697
  // §11.6: don't lie. Wait for the first real frame and report what the
@@ -733,6 +807,32 @@ server.registerTool("crosspad_trace", {
733
807
  const reconciled = action === "add" ? await s.addSignals(signals) : await s.removeSignals(signals);
734
808
  return ok({ action, signals: reconciled });
735
809
  }
810
+ case "write": {
811
+ if (!writes || writes.length === 0)
812
+ return err("write requires `writes` (['target=value', ...]).");
813
+ try {
814
+ const r = await traceWrite(writes);
815
+ return ok({ action, ok: r.ok, results: r.results, error: r.error });
816
+ }
817
+ catch (e) {
818
+ return err(`write failed: ${String(e)}`, { action });
819
+ }
820
+ }
821
+ case "call": {
822
+ if (!func)
823
+ return err("call requires `func` (function symbol).");
824
+ if (!confirm)
825
+ return err("call requires confirm:true (the core is halted for the call).");
826
+ if (args && args.length > 4)
827
+ return err("call accepts at most 4 args (r0-r3).");
828
+ try {
829
+ const r = await traceCall(func, args ?? [], true, ret_type ?? "u32", timeout ?? 2);
830
+ return ok({ action, ...r });
831
+ }
832
+ catch (e) {
833
+ return err(`call failed: ${String(e)}`, { action });
834
+ }
835
+ }
736
836
  }
737
837
  });
738
838
  // ═══════════════════════════════════════════════════════════════════════