9#ifndef YAZE_SRC_CLI_SERVICE_AGENT_TOOL_SCHEMAS_H_
10#define YAZE_SRC_CLI_SERVICE_AGENT_TOOL_SCHEMAS_H_
16#include "absl/strings/str_cat.h"
17#include "absl/strings/str_join.h"
35 std::string
json =
"{";
36 json +=
"\"name\": \"" +
name +
"\", ";
37 json +=
"\"type\": \"" +
type +
"\", ";
39 json +=
"\"required\": " + std::string(
required ?
"true" :
"false");
44 json +=
", \"enum\": [";
75 std::string
json =
"{\n";
76 json +=
" \"name\": \"" +
name +
"\",\n";
81 json +=
" \"parameters\": {\n";
82 json +=
" \"type\": \"object\",\n";
83 json +=
" \"properties\": {\n";
85 for (
size_t i = 0; i <
arguments.size(); ++i) {
87 json +=
" \"" + arg.name +
"\": {\n";
88 json +=
" \"type\": \"" + arg.type +
"\",\n";
89 json +=
" \"description\": \"" + arg.description +
"\"";
90 if (!arg.enum_values.empty()) {
91 json +=
",\n \"enum\": [";
92 for (
size_t j = 0; j < arg.enum_values.size(); ++j) {
93 json +=
"\"" + arg.enum_values[j] +
"\"";
94 if (j < arg.enum_values.size() - 1)
108 json +=
" \"required\": [";
114 json +=
"\"" + arg.name +
"\"";
123 " \"requires_rom\": " + std::string(
requires_rom ?
"true" :
"false") +
126 " \"requires_grpc\": " + std::string(
requires_grpc ?
"true" :
"false");
129 json +=
",\n \"examples\": [\n";
130 for (
size_t i = 0; i <
examples.size(); ++i) {
140 json +=
",\n \"related_tools\": [";
158 md +=
"### " +
name +
"\n\n";
165 md +=
"**Category:** " +
category +
"\n\n";
168 md +=
"**Arguments:**\n\n";
169 md +=
"| Name | Type | Required | Description |\n";
170 md +=
"|------|------|----------|-------------|\n";
172 md +=
"| `" + arg.name +
"` | " + arg.type +
" | ";
173 md += (arg.required ?
"Yes" :
"No") +
" | ";
174 md += arg.description +
" |\n";
180 md +=
"**Examples:**\n\n```bash\n";
188 md +=
"**Related:** " + absl::StrJoin(
related_tools,
", ") +
"\n\n";
209 return it !=
schemas_.end() ? &it->second :
nullptr;
213 std::vector<ToolSchema> result;
214 for (
const auto& [_, schema] :
schemas_) {
215 result.push_back(schema);
221 std::vector<ToolSchema> result;
222 for (
const auto& [_, schema] :
schemas_) {
224 result.push_back(schema);
234 std::string
json =
"[\n";
236 for (
const auto& [_, schema] :
schemas_) {
239 json += schema.ToJson();
250 std::string md =
"# Z3ED Tool Reference\n\n";
253 std::map<std::string, std::vector<const ToolSchema*>> by_category;
254 for (
const auto& [_, schema] :
schemas_) {
255 by_category[schema.category].push_back(&schema);
258 for (
const auto& [
category, tools] : by_category) {
260 for (
const auto*
tool : tools) {
261 md +=
tool->ToMarkdown();
272 std::string prompt =
"## Available Tools\n\n";
274 "You can call the following tools to interact with the ROM and "
277 for (
const auto& [_, schema] :
schemas_) {
278 prompt +=
"- **" + schema.name +
"**: " + schema.description +
"\n";
279 if (!schema.arguments.empty()) {
280 prompt +=
" Arguments: ";
281 for (
size_t i = 0; i < schema.arguments.size(); ++i) {
282 const auto& arg = schema.arguments[i];
286 if (i < schema.arguments.size() - 1)
303 .description =
"List all available tools",
304 .detailed_help =
"Returns a JSON array of all tools the AI can "
305 "call, including their names, categories, and "
306 "brief descriptions.",
308 .examples = {
"z3ed tools-list"},
309 .requires_rom =
false});
313 .description =
"Get detailed information about a specific tool",
314 .arguments = {{.name =
"name",
316 .description =
"Name of the tool to describe",
318 .examples = {
"z3ed tools-describe --name=dungeon-describe-room"},
319 .requires_rom =
false});
323 .description =
"Search tools by keyword",
324 .arguments = {{.name =
"query",
326 .description =
"Search query",
328 .examples = {
"z3ed tools-search --query=sprite"},
329 .requires_rom =
false});
333 .category =
"resource",
334 .description =
"List resource labels for a specific type",
335 .arguments = {{.name =
"type",
337 .description =
"Resource type to list",
339 .enum_values = {
"dungeon",
"overworld",
"sprite",
340 "palette",
"message"}}},
341 .examples = {
"z3ed resource-list --type=dungeon"},
342 .related_tools = {
"resource-search"}});
346 {.name =
"dungeon-list-sprites",
347 .category =
"dungeon",
348 .description =
"List sprites in a dungeon room",
350 "Lists all sprites in the room, including ID, resolved name, "
351 "X/Y, subtype, and layer. Use --sprite-registry to load "
352 "Oracle-of-Secrets custom sprite names.",
353 .arguments = {{.name =
"room",
355 .description =
"Room ID (0-319)",
357 {.name =
"sprite-registry",
360 "Optional path to an Oracle sprite registry "
361 "JSON to resolve custom sprite names",
363 .examples = {
"z3ed dungeon-list-sprites --room=0x77",
364 "z3ed dungeon-list-sprites --room=0x77 "
365 "--sprite-registry=oracle_sprite_registry.json"},
366 .related_tools = {
"dungeon-describe-room",
"dungeon-list-objects"}});
369 {.name =
"dungeon-describe-room",
370 .category =
"dungeon",
371 .description =
"Get detailed description of a dungeon room",
373 "Returns information about room layout, objects, sprites, "
374 "and properties for the specified room ID.",
375 .arguments = {{.name =
"room",
377 .description =
"Room ID (0-295)",
379 .examples = {
"z3ed dungeon-describe-room --room=5"},
380 .related_tools = {
"dungeon-list-sprites",
"dungeon-list-objects"}});
383 {.name =
"dungeon-list-objects",
384 .category =
"dungeon",
385 .description =
"List tile objects in a dungeon room",
387 "Lists all tile objects for a room (ID, X/Y, size, layer).",
388 .arguments = {{.name =
"room",
390 .description =
"Room ID (0-319)",
392 .examples = {
"z3ed dungeon-list-objects --room=0x77"},
393 .related_tools = {
"dungeon-describe-room",
"dungeon-list-sprites"}});
396 {.name =
"dungeon-list-custom-collision",
397 .category =
"dungeon",
398 .description =
"List custom collision tiles for a dungeon room",
400 "Loads the ZScream-style custom collision map (64x64) for a room. "
401 "This is where Oracle-of-Secrets stores minecart tracks/stop "
403 "(B0-BE, B7-BA) and switch tiles (D0-D3). By default, returns "
405 "nonzero entries unless you pass --all or --tiles.",
409 .description =
"Room ID (0-319)",
414 "Comma-separated list of tile values to filter (hex), e.g. "
415 "0xB7,0xB8,0xB9,0xBA",
419 .description =
"If true, return only nonzero collision tiles",
423 .description =
"If true, return all 4096 tiles (including 0x00)",
425 .examples = {
"z3ed dungeon-list-custom-collision --room=0x77",
426 "z3ed dungeon-list-custom-collision --room=0x77 "
427 "--tiles=0xB7,0xB8,0xB9,0xBA"},
428 .related_tools = {
"dungeon-map",
"dungeon-minecart-audit"}});
431 {.name =
"dungeon-export-custom-collision-json",
432 .category =
"dungeon",
433 .description =
"Export custom collision maps to JSON",
435 "Exports per-room custom collision tiles (64x64 map) to a JSON "
436 "authoring format. Supports filtering by --room/--rooms/--all. "
437 "Use --report to emit machine-readable diagnostics to disk.",
438 .arguments = {{.name =
"out",
440 .description =
"Output JSON path",
444 .description =
"Single room ID (hex)",
448 .description =
"Comma-separated room IDs (hex)",
452 .description =
"Export all dungeon rooms (0-319)",
457 "Optional JSON report path for automation",
460 {
"z3ed dungeon-export-custom-collision-json --all "
461 "--out=custom_collision.json",
462 "z3ed dungeon-export-custom-collision-json --rooms=0x25,0x27 "
463 "--out=water_rooms_collision.json"},
464 .related_tools = {
"dungeon-import-custom-collision-json",
465 "dungeon-list-custom-collision",
"dungeon-map"}});
468 {.name =
"dungeon-import-custom-collision-json",
469 .category =
"dungeon",
470 .description =
"Import custom collision maps from JSON",
472 "Imports custom collision room entries from JSON. Writes are "
473 "ROM-safe/fail-closed and reject ROMs without expanded collision "
474 "write support. Use --dry-run for validation-only and --report "
475 "for machine-readable diagnostics. --replace-all requires "
476 "--force unless running --dry-run.",
480 .description =
"Input JSON path",
482 {.name =
"replace-all",
485 "Clear custom collision for rooms not in the JSON file",
490 "Required with --replace-all in write mode (safety gate)",
494 .description =
"Validate and summarize without writing ROM data",
498 .description =
"Optional JSON report path for automation",
500 .examples = {
"z3ed dungeon-import-custom-collision-json "
501 "--in=custom_collision.json --dry-run "
502 "--report=custom_collision.report.json",
503 "z3ed dungeon-import-custom-collision-json "
504 "--in=custom_collision.json --replace-all --force"},
505 .related_tools = {
"dungeon-export-custom-collision-json",
506 "dungeon-list-custom-collision",
507 "dungeon-minecart-audit"}});
510 {.name =
"dungeon-export-water-fill-json",
511 .category =
"dungeon",
512 .description =
"Export water fill zones to JSON",
514 "Exports WaterFill zone data from the reserved ROM table to JSON. "
515 "Supports filtering by --room/--rooms/--all. Use --report to "
516 "emit machine-readable diagnostics to disk.",
517 .arguments = {{.name =
"out",
519 .description =
"Output JSON path",
523 .description =
"Single room ID (hex)",
527 .description =
"Comma-separated room IDs (hex)",
531 .description =
"Export all dungeon rooms (0-319)",
536 "Optional JSON report path for automation",
538 .examples = {
"z3ed dungeon-export-water-fill-json --all "
539 "--out=water_fill_zones.json",
540 "z3ed dungeon-export-water-fill-json --rooms=0x25,0x27 "
541 "--out=d4_water_fill.json"},
542 .related_tools = {
"dungeon-import-water-fill-json",
"rom-doctor"}});
545 {.name =
"dungeon-import-water-fill-json",
546 .category =
"dungeon",
547 .description =
"Import water fill zones from JSON",
549 "Imports WaterFill zones from JSON, normalizes SRAM bit masks, "
550 "and writes to the reserved WaterFill ROM table. Fails closed "
551 "when the reserved region is missing. Use --dry-run for "
552 "validation-only and --strict-masks to fail when normalization "
553 "would be required.",
554 .arguments = {{.name =
"in",
556 .description =
"Input JSON path",
561 "Validate and summarize without writing ROM data",
563 {.name =
"strict-masks",
565 .description =
"Fail if SRAM masks require "
566 "normalization (fail-closed mode)",
571 "Optional JSON report path for automation",
574 {
"z3ed dungeon-import-water-fill-json --in=water_fill_zones.json "
575 "--dry-run --report=water_fill.report.json",
576 "z3ed dungeon-import-water-fill-json --in=water_fill_zones.json "
578 .related_tools = {
"dungeon-export-water-fill-json",
"rom-doctor"}});
581 {.name =
"dungeon-minecart-audit",
582 .category =
"dungeon",
583 .description =
"Audit minecart-related room data",
585 "Checks for minecart track objects (default: 0x31), minecart "
586 "sprites (default: 0xA3), and minecart collision tiles in the "
587 "custom collision map. Emits heuristics for common mismatches.",
592 "Room ID (0-319). Mutually exclusive with rooms/all",
597 "Comma-separated room list (hex), e.g. 0x77,0xA8,0xB8",
601 .description =
"If true, audit all rooms (0-319)",
603 {.name =
"only-issues",
605 .description =
"If true, emit only rooms with issues",
607 {.name =
"only-matches",
610 "If true, emit only rooms with minecart-related data",
612 {.name =
"include-track-objects",
615 "If true, treat track objects as a match even if collision "
616 "data is absent (useful when collision work is unfinished)",
618 {.name =
"track-object-id",
620 .description =
"Track object ID (default: 0x31)",
622 {.name =
"minecart-sprite-id",
624 .description =
"Minecart sprite ID (default: 0xA3)",
626 .examples = {
"z3ed dungeon-minecart-audit --room=0x77 --only-issues",
627 "z3ed dungeon-minecart-audit --rooms=0x77,0xA8,0xB8"},
628 .related_tools = {
"dungeon-list-sprites",
"dungeon-list-objects",
629 "dungeon-list-custom-collision",
"dungeon-map"}});
633 {.name =
"overworld-describe-map",
634 .category =
"overworld",
635 .description =
"Get detailed description of an overworld map",
636 .arguments = {{.name =
"map",
638 .description =
"Map ID (0-159)",
640 .examples = {
"z3ed overworld-describe-map --map=0"},
641 .related_tools = {
"overworld-list-sprites",
"overworld-find-tile"}});
644 Register({.name =
"filesystem-list",
645 .category =
"filesystem",
646 .description =
"List directory contents",
647 .arguments = {{.name =
"path",
649 .description =
"Directory path to list",
651 .examples = {
"z3ed filesystem-list --path=src/app"},
652 .requires_rom =
false,
653 .related_tools = {
"filesystem-read",
"filesystem-exists"}});
655 Register({.name =
"filesystem-read",
656 .category =
"filesystem",
657 .description =
"Read file contents",
658 .arguments = {{.name =
"path",
660 .description =
"File path to read",
664 .description =
"Maximum lines to read",
666 .default_value =
"100"}},
667 .examples = {
"z3ed filesystem-read --path=src/app/rom.h"},
668 .requires_rom =
false});
672 .category =
"memory",
673 .description =
"List known ALTTP memory regions",
674 .detailed_help =
"Returns a list of known memory regions in A "
675 "Link to the Past, including player state, "
676 "sprites, save data, and system variables.",
678 .examples = {
"z3ed memory-regions"},
679 .requires_rom =
false,
680 .related_tools = {
"memory-analyze",
"memory-search"}});
683 Register({.name =
"tools-harness-state",
685 .description =
"Generate WRAM state for test harnesses",
687 "Runs the emulator to the main game loop and dumps the "
688 "complete WRAM state and register values to a C++ header "
689 "file for use in test harnesses.",
690 .arguments = {{.name =
"rom",
692 .description =
"Path to ROM file",
696 .description =
"Output header file path",
698 .examples = {
"z3ed tools-harness-state --rom=zelda3.sfc "
699 "--output=harness_state.h"},
700 .requires_rom =
false});
702 Register({.name =
"tools-extract-golden",
704 .description =
"Extract comprehensive golden data from ROM",
705 .arguments = {{.name =
"rom",
707 .description =
"Path to ROM file",
711 .description =
"Output header file path",
713 .examples = {
"z3ed tools-extract-golden --rom=zelda3.sfc "
714 "--output=golden_data.h"}});
717 Register({.name =
"visual-find-similar-tiles",
718 .category =
"visual",
719 .description =
"Find tiles with similar patterns to a reference",
721 "Compares a reference tile against all tiles in graphics "
722 "sheets and returns matches above the similarity threshold. "
723 "Useful for finding duplicate or near-duplicate tiles for "
725 .arguments = {{.name =
"tile_id",
727 .description =
"Reference tile ID to compare",
731 .description =
"Graphics sheet index (0-222)",
733 .default_value =
"0"},
734 {.name =
"threshold",
736 .description =
"Minimum similarity score (0-100)",
738 .default_value =
"80"},
741 .description =
"Comparison method",
743 .default_value =
"structural",
744 .enum_values = {
"pixel",
"structural"}}},
745 .examples = {
"z3ed visual-find-similar-tiles --tile_id=42",
746 "z3ed visual-find-similar-tiles --tile_id=10 "
747 "--sheet=5 --threshold=90"},
748 .related_tools = {
"visual-analyze-spritesheet",
749 "visual-tile-histogram"}});
751 Register({.name =
"visual-analyze-spritesheet",
752 .category =
"visual",
753 .description =
"Identify unused regions in graphics sheets",
755 "Scans graphics sheets for contiguous empty regions that "
756 "can be used for custom graphics in ROM hacking. Reports "
757 "the location and size of each free region.",
758 .arguments = {{.name =
"sheet",
761 "Specific sheet to analyze (all if omitted)",
763 {.name =
"tile_size",
765 .description =
"Tile size to check (8 or 16)",
767 .default_value =
"8",
768 .enum_values = {
"8",
"16"}}},
769 .examples = {
"z3ed visual-analyze-spritesheet",
770 "z3ed visual-analyze-spritesheet --sheet=10 "
772 .related_tools = {
"visual-find-similar-tiles"}});
774 Register({.name =
"visual-palette-usage",
775 .category =
"visual",
776 .description =
"Analyze palette usage statistics across maps",
778 "Analyzes which palette indices are used across overworld "
779 "maps and dungeon rooms. Helps identify under-utilized "
780 "palettes and optimization opportunities.",
781 .arguments = {{.name =
"type",
783 .description =
"Map type to analyze",
785 .default_value =
"all",
786 .enum_values = {
"overworld",
"dungeon",
"all"}}},
787 .examples = {
"z3ed visual-palette-usage",
788 "z3ed visual-palette-usage --type=dungeon"},
789 .related_tools = {
"visual-tile-histogram"}});
792 {.name =
"visual-tile-histogram",
793 .category =
"visual",
794 .description =
"Generate frequency histogram of tile usage",
796 "Counts the frequency of each tile ID used across tilemaps "
797 "to identify commonly and rarely used tiles. Useful for "
798 "understanding tile distribution and finding candidates "
800 .arguments = {{.name =
"type",
802 .description =
"Map type to analyze",
804 .default_value =
"overworld",
805 .enum_values = {
"overworld",
"dungeon"}},
808 .description =
"Number of top entries to return",
810 .default_value =
"20"}},
811 .examples = {
"z3ed visual-tile-histogram",
812 "z3ed visual-tile-histogram --type=dungeon --top=50"},
813 .related_tools = {
"visual-palette-usage",
814 "visual-find-similar-tiles"}});
821 {.name =
"codegen-asm-hook",
822 .category =
"codegen",
823 .description =
"Generate ASM hook at ROM address",
825 "Generates Asar-compatible ASM code to hook into the ROM at "
826 "a specified address using JSL. Validates the address is safe "
827 "and not already hooked. Includes known safe hook locations.",
828 .arguments = {{.name =
"address",
830 .description =
"ROM address to hook (hex)",
834 .description =
"Label name for the hook",
838 .description =
"Number of NOP bytes to add",
840 .default_value =
"1"}},
841 .examples = {
"z3ed codegen-asm-hook --address=0x02AB08 --label=MyHook",
842 "z3ed codegen-asm-hook --address=0x00893D "
843 "--label=ForceBlankHook --nop-fill=2"},
844 .requires_rom =
true,
845 .related_tools = {
"codegen-freespace-patch",
"memory-analyze"}});
848 {.name =
"codegen-freespace-patch",
849 .category =
"codegen",
850 .description =
"Generate patch using detected free regions",
852 "Detects available freespace in the ROM (regions with >80% "
853 "0x00/0xFF bytes) and generates a patch to allocate space "
854 "for custom code. Returns available regions and generated ASM.",
855 .arguments = {{.name =
"label",
857 .description =
"Label for the code block",
861 .description =
"Size in bytes needed (hex)",
863 {.name =
"prefer-bank",
865 .description =
"Preferred bank number (hex)",
868 {
"z3ed codegen-freespace-patch --label=MyCode --size=0x100",
869 "z3ed codegen-freespace-patch --label=CustomRoutine --size=0x200 "
870 "--prefer-bank=0x3F"},
871 .requires_rom =
true,
872 .related_tools = {
"codegen-asm-hook",
"memory-regions"}});
874 Register({.name =
"codegen-sprite-template",
875 .category =
"codegen",
876 .description =
"Generate sprite ASM from template",
878 "Generates a complete sprite ASM template with init and main "
879 "state machine. Includes SNES sprite variable documentation "
880 "and proper PHB/PLB register preservation.",
881 .arguments = {{.name =
"name",
883 .description =
"Sprite name/label",
885 {.name =
"init-code",
887 .description =
"Initialization ASM code",
889 {.name =
"main-code",
891 .description =
"Main loop ASM code",
893 .examples = {
"z3ed codegen-sprite-template --name=MySprite",
894 "z3ed codegen-sprite-template --name=CustomChest "
895 "--init-code=\"LDA #$42 : STA $0DC0,X\""},
896 .requires_rom =
false,
897 .related_tools = {
"codegen-event-handler"}});
900 {.name =
"codegen-event-handler",
901 .category =
"codegen",
902 .description =
"Generate event handler code",
904 "Generates ASM event handler code for NMI, IRQ, or Reset "
905 "handlers. Includes proper state preservation and known "
906 "hook addresses for each event type.",
907 .arguments = {{.name =
"type",
909 .description =
"Event type",
911 .enum_values = {
"nmi",
"irq",
"reset"}},
914 .description =
"Handler label name",
916 {.name =
"custom-code",
918 .description =
"Custom ASM code",
920 .examples = {
"z3ed codegen-event-handler --type=nmi --label=MyVBlank",
921 "z3ed codegen-event-handler --type=nmi --label=MyHandler "
922 "--custom-code=\"LDA #$80 : STA $2100\""},
923 .requires_rom =
false,
924 .related_tools = {
"codegen-asm-hook",
"codegen-sprite-template"}});
931 .category =
"project",
932 .description =
"Show current project state and pending edits",
934 "Displays current project state including loaded ROM info, "
935 "pending uncommitted edits, available snapshots, and ROM "
936 "checksum for validation.",
938 .examples = {
"z3ed project-status"},
939 .requires_rom =
true,
940 .related_tools = {
"project-snapshot",
"project-restore"}});
942 Register({.name =
"project-snapshot",
943 .category =
"project",
944 .description =
"Create named checkpoint with edit deltas",
946 "Creates a named snapshot storing all pending edits as "
947 "deltas (not full ROM copy). Includes ROM checksum for "
948 "validation when restoring.",
949 .arguments = {{.name =
"name",
951 .description =
"Snapshot name",
953 {.name =
"description",
955 .description =
"Optional description",
957 .examples = {
"z3ed project-snapshot --name=before-edit",
958 "z3ed project-snapshot --name=dungeon-complete "
959 "--description=\"Finished dungeon 1\""},
960 .requires_rom =
true,
961 .related_tools = {
"project-status",
"project-restore",
964 Register({.name =
"project-restore",
965 .category =
"project",
966 .description =
"Restore ROM to named checkpoint",
968 "Restores the ROM to a previously saved snapshot state by "
969 "replaying the stored edit deltas. Validates ROM checksum "
970 "to ensure correct base ROM.",
971 .arguments = {{.name =
"name",
973 .description =
"Snapshot name to restore",
975 .examples = {
"z3ed project-restore --name=before-edit"},
976 .requires_rom =
true,
977 .related_tools = {
"project-snapshot",
"project-status"}});
980 {.name =
"project-export",
981 .category =
"project",
982 .description =
"Export project as portable archive",
984 "Exports the project metadata and all snapshots as a "
985 "portable archive file. Optionally includes the base ROM.",
986 .arguments = {{.name =
"path",
988 .description =
"Output file path",
990 {.name =
"include-rom",
992 .description =
"Include base ROM in export",
994 .examples = {
"z3ed project-export --path=myproject.tar.gz",
995 "z3ed project-export --path=backup.tar.gz --include-rom"},
996 .requires_rom =
true,
997 .related_tools = {
"project-import"}});
1000 .category =
"project",
1001 .description =
"Import project archive",
1003 "Imports a project archive and loads its metadata and "
1004 "snapshots. Validates project structure and checksums.",
1005 .arguments = {{.name =
"path",
1007 .description =
"Archive file path",
1009 .examples = {
"z3ed project-import --path=myproject.tar.gz"},
1010 .requires_rom =
false,
1011 .related_tools = {
"project-export"}});
1014 {.name =
"project-diff",
1015 .category =
"project",
1016 .description =
"Compare two project states",
1018 "Compares two snapshots and shows the differences in edits "
1019 "between them. Useful for reviewing changes between versions.",
1020 .arguments = {{.name =
"snapshot1",
1022 .description =
"First snapshot name",
1024 {.name =
"snapshot2",
1026 .description =
"Second snapshot name",
1028 .examples = {
"z3ed project-diff --snapshot1=v1 --snapshot2=v2"},
1029 .requires_rom =
true,
1030 .related_tools = {
"project-snapshot",
"rom-diff"}});
1036 Register({.name =
"mesen-gamestate",
1037 .category =
"mesen2",
1038 .description =
"Get ALTTP game state from running Mesen2",
1040 "Queries the Mesen2 socket API for comprehensive ALTTP game "
1041 "state including Link's position, direction, health, items, "
1042 "and current game mode. Requires Mesen2-OoS running.",
1044 .examples = {
"z3ed mesen-gamestate"},
1045 .requires_rom =
false,
1046 .requires_grpc =
true,
1047 .related_tools = {
"mesen-sprites",
"mesen-cpu"}});
1050 .category =
"mesen2",
1051 .description =
"Get active sprites from running Mesen2",
1053 "Queries sprite table from Mesen2 to show all active sprites "
1054 "with their type, position, health, and state. Useful for "
1055 "debugging sprite behavior and interactions.",
1056 .arguments = {{.name =
"all",
1058 .description =
"Include inactive sprite slots",
1060 .default_value =
"false"}},
1061 .examples = {
"z3ed mesen-sprites",
"z3ed mesen-sprites --all"},
1062 .requires_rom =
false,
1063 .requires_grpc =
true,
1064 .related_tools = {
"mesen-gamestate",
"mesen-memory-read"}});
1067 .category =
"mesen2",
1068 .description =
"Get CPU register state from Mesen2",
1070 "Returns current 65816 CPU register state: A, X, Y, SP, D, "
1071 "PC, K (program bank), DBR (data bank), and P (processor "
1072 "status). Useful for debugging at breakpoints.",
1074 .examples = {
"z3ed mesen-cpu"},
1075 .requires_rom =
false,
1076 .requires_grpc =
true,
1077 .related_tools = {
"mesen-gamestate",
"mesen-disasm"}});
1080 {.name =
"mesen-memory-read",
1081 .category =
"mesen2",
1082 .description =
"Read memory from Mesen2 emulator",
1084 "Reads a block of memory from the running Mesen2 instance. "
1085 "Returns hex dump of the specified region. Useful for "
1086 "inspecting live game state.",
1087 .arguments = {{.name =
"address",
1089 .description =
"Start address (hex)",
1093 .description =
"Number of bytes to read",
1095 .default_value =
"16"}},
1096 .examples = {
"z3ed mesen-memory-read --address=0x7E0020 --length=16"},
1097 .requires_rom =
false,
1098 .requires_grpc =
true,
1099 .related_tools = {
"mesen-memory-write",
"memory-analyze"}});
1102 {.name =
"mesen-memory-write",
1103 .category =
"mesen2",
1104 .description =
"Write memory in Mesen2 emulator",
1106 "Writes bytes to memory in the running Mesen2 instance. "
1107 "Useful for testing ROM patches or modifying game state.",
1108 .arguments = {{.name =
"address",
1110 .description =
"Target address (hex)",
1114 .description =
"Hex bytes to write",
1116 .examples = {
"z3ed mesen-memory-write --address=0x7EF36D --data=A0"},
1117 .requires_rom =
false,
1118 .requires_grpc =
true,
1119 .related_tools = {
"mesen-memory-read"}});
1122 .category =
"mesen2",
1123 .description =
"Disassemble code at address in Mesen2",
1125 "Disassembles instructions at the specified address using "
1126 "Mesen2's built-in disassembler, which uses loaded symbols "
1128 .arguments = {{.name =
"address",
1130 .description =
"Start address (hex)",
1134 .description =
"Number of instructions",
1136 .default_value =
"10"}},
1137 .examples = {
"z3ed mesen-disasm --address=0x008000 --count=20"},
1138 .requires_rom =
false,
1139 .requires_grpc =
true,
1140 .related_tools = {
"mesen-cpu",
"mesen-trace"}});
1143 .category =
"mesen2",
1144 .description =
"Get execution trace from Mesen2",
1146 "Returns the last N executed instructions from Mesen2's "
1147 "trace log. Useful for understanding control flow leading "
1148 "to a crash or unexpected behavior.",
1149 .arguments = {{.name =
"count",
1151 .description =
"Number of trace entries",
1153 .default_value =
"20"}},
1154 .examples = {
"z3ed mesen-trace",
"z3ed mesen-trace --count=50"},
1155 .requires_rom =
false,
1156 .requires_grpc =
true,
1157 .related_tools = {
"mesen-disasm",
"mesen-cpu"}});
1160 {.name =
"mesen-breakpoint",
1161 .category =
"mesen2",
1162 .description =
"Manage breakpoints in Mesen2",
1164 "Add, remove, or list breakpoints in the running Mesen2 "
1165 "instance. Supports execution, read, and write breakpoints.",
1166 .arguments = {{.name =
"action",
1168 .description =
"Breakpoint action",
1170 .enum_values = {
"add",
"remove",
"clear",
"list"}},
1173 .description =
"Address for add/remove",
1177 .description =
"Breakpoint type",
1179 .default_value =
"exec",
1180 .enum_values = {
"exec",
"read",
"write",
"rw"}}},
1181 .examples = {
"z3ed mesen-breakpoint --action=add --address=0x008000",
1182 "z3ed mesen-breakpoint --action=clear"},
1183 .requires_rom =
false,
1184 .requires_grpc =
true,
1185 .related_tools = {
"mesen-cpu",
"mesen-trace"}});
1188 .category =
"mesen2",
1189 .description =
"Control Mesen2 emulation state",
1191 "Pause, resume, step, or frame advance the running Mesen2 "
1192 "instance. For automated testing and debugging workflows.",
1193 .arguments = {{.name =
"action",
1195 .description =
"Control action",
1197 .enum_values = {
"pause",
"resume",
"step",
"frame",
1199 .examples = {
"z3ed mesen-control --action=pause",
1200 "z3ed mesen-control --action=frame"},
1201 .requires_rom =
false,
1202 .requires_grpc =
true,
1203 .related_tools = {
"mesen-cpu",
"mesen-gamestate"}});
1206 {.name =
"mesen-session",
1207 .category =
"mesen2",
1208 .description =
"Read tracked autonomous emulator session state",
1210 "Returns or manages the command-side session snapshot used for "
1211 "deterministic automation: connection state, run/pause state, "
1212 "frame, last PC, tracked breakpoints, and last action.",
1213 .arguments = {{.name =
"action",
1216 "Session action (default: show). export/import "
1219 .enum_values = {
"show",
"reset",
"export",
"import"}},
1223 "Session JSON path used by --action=export/import",
1224 .required =
false}},
1225 .examples = {
"z3ed mesen-session",
"z3ed mesen-session --action=reset",
1226 "z3ed mesen-session --action=export --file=/tmp/s.json"},
1227 .requires_rom =
false,
1228 .requires_grpc =
true,
1229 .related_tools = {
"mesen-await",
"mesen-goal",
"mesen-control"}});
1232 {.name =
"mesen-await",
1233 .category =
"mesen2",
1234 .description =
"Block until frame/pc/breakpoint condition is met",
1236 "Polling wait primitive for deterministic automation. Waits until "
1237 "a frame delta is reached, CPU PC matches an address, or a "
1239 "breakpoint ID is hit.",
1240 .arguments = {{.name =
"type",
1242 .description =
"Await condition type",
1244 .enum_values = {
"frame",
"pc",
"breakpoint"}},
1247 .description =
"Frame delta when --type=frame",
1251 .description =
"Target PC when --type=pc",
1255 .description =
"Breakpoint ID when --type=breakpoint",
1257 {.name =
"timeout-ms",
1259 .description =
"Timeout in milliseconds",
1261 .default_value =
"2000"},
1264 .description =
"Polling interval in milliseconds",
1266 .default_value =
"25"}},
1267 .examples = {
"z3ed mesen-await --type=frame --count=60",
1268 "z3ed mesen-await --type=pc --address=0x028000"},
1269 .requires_rom =
false,
1270 .requires_grpc =
true,
1271 .related_tools = {
"mesen-breakpoint",
"mesen-goal",
"mesen-session"}});
1274 {.name =
"mesen-goal",
1275 .category =
"mesen2",
1276 .description =
"Execute deterministic emulator control macros",
1278 "Runs atomic multi-step workflows for debugging: break-at "
1279 "(pause, add breakpoint, resume, wait for hit, pause, cleanup), "
1280 "run-frames (advance by N frames), and capture-state-at-pc "
1281 "(break-at plus game state capture).",
1282 .arguments = {{.name =
"goal",
1284 .description =
"Goal macro name",
1286 .enum_values = {
"break-at",
"run-frames",
1287 "capture-state-at-pc"}},
1291 "Target PC for break-at/capture-state-at-pc",
1295 .description =
"Frame count for run-frames",
1297 {.name =
"timeout-ms",
1299 .description =
"Timeout in milliseconds",
1301 .default_value =
"2000"},
1304 .description =
"Polling interval in milliseconds",
1306 .default_value =
"25"}},
1307 .examples = {
"z3ed mesen-goal --goal=break-at --address=0x008000",
1308 "z3ed mesen-goal --goal=run-frames --count=120",
1309 "z3ed mesen-goal --goal=capture-state-at-pc "
1310 "--address=0x028000"},
1311 .requires_rom =
false,
1312 .requires_grpc =
true,
1313 .related_tools = {
"mesen-await",
"mesen-breakpoint",
1317 {.name =
"mesen-state-verify",
1318 .category =
"mesen2",
1320 "Verify savestate freshness against ROM hash and metadata",
1322 "Loads a sidecar metadata JSON and validates that the savestate "
1323 "file hash and ROM hash match the current files. Returns non-zero "
1324 "if stale or mismatched.",
1328 .description =
"Savestate file path",
1330 {.name =
"rom-file",
1332 .description =
"ROM file path to validate against",
1337 "Optional metadata path (default: <state>.meta.json)",
1339 {.name =
"scenario",
1341 .description =
"Optional scenario ID expectation",
1342 .required =
false}},
1343 .examples = {
"z3ed mesen-state-verify --state=foo.state "
1344 "--rom-file=Roms/oos168x.sfc",
1345 "z3ed mesen-state-verify --state=foo.state "
1346 "--rom-file=Roms/oos168x.sfc --scenario=d6_room_88"},
1347 .requires_rom =
false,
1348 .requires_grpc =
false,
1349 .related_tools = {
"mesen-state-regen",
"mesen-session",
1353 {.name =
"mesen-state-regen",
1354 .category =
"mesen2",
1355 .description =
"Regenerate savestate metadata for freshness checks",
1357 "Writes a metadata sidecar (hashes + optional runtime info) for "
1358 "a savestate file. Use with mesen-state-verify to pin fixtures to "
1359 "a specific ROM hash.",
1360 .arguments = {{.name =
"state",
1362 .description =
"Savestate file path",
1364 {.name =
"rom-file",
1366 .description =
"ROM file path",
1370 .description =
"Optional output metadata path "
1371 "(default: <state>.meta.json)",
1373 {.name =
"scenario",
1375 .description =
"Optional scenario ID to store",
1376 .required =
false}},
1377 .examples = {
"z3ed mesen-state-regen --state=foo.state "
1378 "--rom-file=Roms/oos168x.sfc",
1379 "z3ed mesen-state-regen --state=foo.state "
1380 "--rom-file=Roms/oos168x.sfc --scenario=d6_room_88"},
1381 .requires_rom =
false,
1382 .requires_grpc =
false,
1383 .related_tools = {
"mesen-state-verify",
"mesen-session",
1387 {.name =
"mesen-state-capture",
1388 .category =
"mesen2",
1389 .description =
"Capture savestate metadata in one command",
1391 "Creates or refreshes a savestate+metadata fixture. Can trigger a "
1392 "Mesen slot save first, then hash and write metadata for the "
1393 "target state path.",
1397 .description =
"Target savestate path",
1399 {.name =
"rom-file",
1401 .description =
"ROM file path",
1405 .description =
"Optional output metadata path "
1406 "(default: <state>.meta.json)",
1408 {.name =
"scenario",
1410 .description =
"Optional scenario ID to store",
1414 .description =
"Optional Mesen save slot to trigger before "
1417 {.name =
"states-dir",
1419 .description =
"Optional directory to auto-locate latest "
1420 ".state when target path is missing",
1424 .description =
"Delay after slot save before file capture",
1426 .default_value =
"400"}},
1428 {
"z3ed mesen-state-capture --state=foo.state "
1429 "--rom-file=Roms/oos168x.sfc --scenario=d6_room_88",
1430 "z3ed mesen-state-capture --state=Roms/SaveStates/d6.state "
1431 "--rom-file=Roms/oos168x.sfc --slot=1 "
1432 "--states-dir=~/Library/Application\\ Support/Mesen2/SaveStates"},
1433 .requires_rom =
false,
1434 .requires_grpc =
false,
1435 .related_tools = {
"mesen-state-verify",
"mesen-state-regen",
1439 {.name =
"mesen-state-hook",
1440 .category =
"mesen2",
1441 .description =
"Agent preflight hook for savestate freshness",
1443 "Returns a compact preflight summary (hashes, scenario, reasons) "
1444 "and exits non-zero when stale. Intended for automation wrappers "
1445 "that run before any emulator action.",
1449 .description =
"Savestate file path",
1451 {.name =
"rom-file",
1453 .description =
"ROM file path",
1458 "Optional metadata path (default: <state>.meta.json)",
1460 {.name =
"scenario",
1462 .description =
"Optional scenario ID expectation",
1463 .required =
false}},
1464 .examples = {
"z3ed mesen-state-hook --state=foo.state "
1465 "--rom-file=Roms/oos168x.sfc --scenario=d6_room_88",
1466 "z3ed mesen-state-hook --state=foo.state "
1467 "--rom-file=Roms/oos168x.sfc --format=json"},
1468 .requires_rom =
false,
1469 .requires_grpc =
false,
1470 .related_tools = {
"mesen-state-verify",
"mesen-state-capture"}});
Get detailed information about a tool
Argument schema for a tool parameter.
std::vector< std::string > enum_values
std::string ToJson() const
std::string default_value