yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
tool_schemas.h
Go to the documentation of this file.
1
9#ifndef YAZE_SRC_CLI_SERVICE_AGENT_TOOL_SCHEMAS_H_
10#define YAZE_SRC_CLI_SERVICE_AGENT_TOOL_SCHEMAS_H_
11
12#include <map>
13#include <string>
14#include <vector>
15
16#include "absl/strings/str_cat.h"
17#include "absl/strings/str_join.h"
18
19namespace yaze {
20namespace cli {
21namespace agent {
22
27 std::string name;
28 std::string type; // "string", "number", "boolean", "enum"
29 std::string description;
30 bool required = false;
31 std::string default_value;
32 std::vector<std::string> enum_values; // For enum type
33
34 std::string ToJson() const {
35 std::string json = "{";
36 json += "\"name\": \"" + name + "\", ";
37 json += "\"type\": \"" + type + "\", ";
38 json += "\"description\": \"" + description + "\", ";
39 json += "\"required\": " + std::string(required ? "true" : "false");
40 if (!default_value.empty()) {
41 json += ", \"default\": \"" + default_value + "\"";
42 }
43 if (!enum_values.empty()) {
44 json += ", \"enum\": [";
45 for (size_t i = 0; i < enum_values.size(); ++i) {
46 json += "\"" + enum_values[i] + "\"";
47 if (i < enum_values.size() - 1)
48 json += ", ";
49 }
50 json += "]";
51 }
52 json += "}";
53 return json;
54 }
55};
56
60struct ToolSchema {
61 std::string name;
62 std::string category;
63 std::string description;
64 std::string detailed_help;
65 std::vector<ArgumentSchema> arguments;
66 std::vector<std::string> examples;
67 std::vector<std::string> related_tools;
68 bool requires_rom = true;
69 bool requires_grpc = false;
70
74 std::string ToJson() const {
75 std::string json = "{\n";
76 json += " \"name\": \"" + name + "\",\n";
77 json += " \"description\": \"" + description + "\",\n";
78 json += " \"category\": \"" + category + "\",\n";
79
80 // Parameters
81 json += " \"parameters\": {\n";
82 json += " \"type\": \"object\",\n";
83 json += " \"properties\": {\n";
84
85 for (size_t i = 0; i < arguments.size(); ++i) {
86 const auto& arg = arguments[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)
95 json += ", ";
96 }
97 json += "]";
98 }
99 json += "\n }";
100 if (i < arguments.size() - 1)
101 json += ",";
102 json += "\n";
103 }
104
105 json += " },\n";
106
107 // Required arguments
108 json += " \"required\": [";
109 bool first = true;
110 for (const auto& arg : arguments) {
111 if (arg.required) {
112 if (!first)
113 json += ", ";
114 json += "\"" + arg.name + "\"";
115 first = false;
116 }
117 }
118 json += "]\n";
119 json += " },\n";
120
121 // Additional metadata
122 json +=
123 " \"requires_rom\": " + std::string(requires_rom ? "true" : "false") +
124 ",\n";
125 json +=
126 " \"requires_grpc\": " + std::string(requires_grpc ? "true" : "false");
127
128 if (!examples.empty()) {
129 json += ",\n \"examples\": [\n";
130 for (size_t i = 0; i < examples.size(); ++i) {
131 json += " \"" + examples[i] + "\"";
132 if (i < examples.size() - 1)
133 json += ",";
134 json += "\n";
135 }
136 json += " ]";
137 }
138
139 if (!related_tools.empty()) {
140 json += ",\n \"related_tools\": [";
141 for (size_t i = 0; i < related_tools.size(); ++i) {
142 json += "\"" + related_tools[i] + "\"";
143 if (i < related_tools.size() - 1)
144 json += ", ";
145 }
146 json += "]";
147 }
148
149 json += "\n}";
150 return json;
151 }
152
156 std::string ToMarkdown() const {
157 std::string md;
158 md += "### " + name + "\n\n";
159 md += description + "\n\n";
160
161 if (!detailed_help.empty()) {
162 md += detailed_help + "\n\n";
163 }
164
165 md += "**Category:** " + category + "\n\n";
166
167 if (!arguments.empty()) {
168 md += "**Arguments:**\n\n";
169 md += "| Name | Type | Required | Description |\n";
170 md += "|------|------|----------|-------------|\n";
171 for (const auto& arg : arguments) {
172 md += "| `" + arg.name + "` | " + arg.type + " | ";
173 md += (arg.required ? "Yes" : "No") + " | ";
174 md += arg.description + " |\n";
175 }
176 md += "\n";
177 }
178
179 if (!examples.empty()) {
180 md += "**Examples:**\n\n```bash\n";
181 for (const auto& ex : examples) {
182 md += ex + "\n";
183 }
184 md += "```\n\n";
185 }
186
187 if (!related_tools.empty()) {
188 md += "**Related:** " + absl::StrJoin(related_tools, ", ") + "\n\n";
189 }
190
191 return md;
192 }
193};
194
199 public:
201 static ToolSchemaRegistry instance;
202 return instance;
203 }
204
205 void Register(const ToolSchema& schema) { schemas_[schema.name] = schema; }
206
207 const ToolSchema* Get(const std::string& name) const {
208 auto it = schemas_.find(name);
209 return it != schemas_.end() ? &it->second : nullptr;
210 }
211
212 std::vector<ToolSchema> GetAll() const {
213 std::vector<ToolSchema> result;
214 for (const auto& [_, schema] : schemas_) {
215 result.push_back(schema);
216 }
217 return result;
218 }
219
220 std::vector<ToolSchema> GetByCategory(const std::string& category) const {
221 std::vector<ToolSchema> result;
222 for (const auto& [_, schema] : schemas_) {
223 if (schema.category == category) {
224 result.push_back(schema);
225 }
226 }
227 return result;
228 }
229
233 std::string ExportAllAsJson() const {
234 std::string json = "[\n";
235 bool first = true;
236 for (const auto& [_, schema] : schemas_) {
237 if (!first)
238 json += ",\n";
239 json += schema.ToJson();
240 first = false;
241 }
242 json += "\n]";
243 return json;
244 }
245
249 std::string ExportAllAsMarkdown() const {
250 std::string md = "# Z3ED Tool Reference\n\n";
251
252 // Group by category
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);
256 }
257
258 for (const auto& [category, tools] : by_category) {
259 md += "## " + category + "\n\n";
260 for (const auto* tool : tools) {
261 md += tool->ToMarkdown();
262 }
263 }
264
265 return md;
266 }
267
271 std::string GenerateLLMPrompt() const {
272 std::string prompt = "## Available Tools\n\n";
273 prompt +=
274 "You can call the following tools to interact with the ROM and "
275 "editor:\n\n";
276
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];
283 prompt += arg.name;
284 if (arg.required)
285 prompt += "*";
286 if (i < schema.arguments.size() - 1)
287 prompt += ", ";
288 }
289 prompt += "\n";
290 }
291 }
292
293 return prompt;
294 }
295
296 private:
298
300 // Meta-tools
301 Register({.name = "tools-list",
302 .category = "meta",
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.",
307 .arguments = {},
308 .examples = {"z3ed tools-list"},
309 .requires_rom = false});
310
311 Register({.name = "tools-describe",
312 .category = "meta",
313 .description = "Get detailed information about a specific tool",
314 .arguments = {{.name = "name",
315 .type = "string",
316 .description = "Name of the tool to describe",
317 .required = true}},
318 .examples = {"z3ed tools-describe --name=dungeon-describe-room"},
319 .requires_rom = false});
320
321 Register({.name = "tools-search",
322 .category = "meta",
323 .description = "Search tools by keyword",
324 .arguments = {{.name = "query",
325 .type = "string",
326 .description = "Search query",
327 .required = true}},
328 .examples = {"z3ed tools-search --query=sprite"},
329 .requires_rom = false});
330
331 // Resource tools
332 Register({.name = "resource-list",
333 .category = "resource",
334 .description = "List resource labels for a specific type",
335 .arguments = {{.name = "type",
336 .type = "string",
337 .description = "Resource type to list",
338 .required = true,
339 .enum_values = {"dungeon", "overworld", "sprite",
340 "palette", "message"}}},
341 .examples = {"z3ed resource-list --type=dungeon"},
342 .related_tools = {"resource-search"}});
343
344 // Dungeon tools
345 Register(
346 {.name = "dungeon-list-sprites",
347 .category = "dungeon",
348 .description = "List sprites in a dungeon room",
349 .detailed_help =
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",
354 .type = "number",
355 .description = "Room ID (0-319)",
356 .required = true},
357 {.name = "sprite-registry",
358 .type = "string",
359 .description =
360 "Optional path to an Oracle sprite registry "
361 "JSON to resolve custom sprite names",
362 .required = false}},
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"}});
367
368 Register(
369 {.name = "dungeon-describe-room",
370 .category = "dungeon",
371 .description = "Get detailed description of a dungeon room",
372 .detailed_help =
373 "Returns information about room layout, objects, sprites, "
374 "and properties for the specified room ID.",
375 .arguments = {{.name = "room",
376 .type = "number",
377 .description = "Room ID (0-295)",
378 .required = true}},
379 .examples = {"z3ed dungeon-describe-room --room=5"},
380 .related_tools = {"dungeon-list-sprites", "dungeon-list-objects"}});
381
382 Register(
383 {.name = "dungeon-list-objects",
384 .category = "dungeon",
385 .description = "List tile objects in a dungeon room",
386 .detailed_help =
387 "Lists all tile objects for a room (ID, X/Y, size, layer).",
388 .arguments = {{.name = "room",
389 .type = "number",
390 .description = "Room ID (0-319)",
391 .required = true}},
392 .examples = {"z3ed dungeon-list-objects --room=0x77"},
393 .related_tools = {"dungeon-describe-room", "dungeon-list-sprites"}});
394
395 Register(
396 {.name = "dungeon-list-custom-collision",
397 .category = "dungeon",
398 .description = "List custom collision tiles for a dungeon room",
399 .detailed_help =
400 "Loads the ZScream-style custom collision map (64x64) for a room. "
401 "This is where Oracle-of-Secrets stores minecart tracks/stop "
402 "tiles "
403 "(B0-BE, B7-BA) and switch tiles (D0-D3). By default, returns "
404 "only "
405 "nonzero entries unless you pass --all or --tiles.",
406 .arguments =
407 {{.name = "room",
408 .type = "number",
409 .description = "Room ID (0-319)",
410 .required = true},
411 {.name = "tiles",
412 .type = "string",
413 .description =
414 "Comma-separated list of tile values to filter (hex), e.g. "
415 "0xB7,0xB8,0xB9,0xBA",
416 .required = false},
417 {.name = "nonzero",
418 .type = "boolean",
419 .description = "If true, return only nonzero collision tiles",
420 .required = false},
421 {.name = "all",
422 .type = "boolean",
423 .description = "If true, return all 4096 tiles (including 0x00)",
424 .required = false}},
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"}});
429
430 Register(
431 {.name = "dungeon-export-custom-collision-json",
432 .category = "dungeon",
433 .description = "Export custom collision maps to JSON",
434 .detailed_help =
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",
439 .type = "string",
440 .description = "Output JSON path",
441 .required = true},
442 {.name = "room",
443 .type = "number",
444 .description = "Single room ID (hex)",
445 .required = false},
446 {.name = "rooms",
447 .type = "string",
448 .description = "Comma-separated room IDs (hex)",
449 .required = false},
450 {.name = "all",
451 .type = "boolean",
452 .description = "Export all dungeon rooms (0-319)",
453 .required = false},
454 {.name = "report",
455 .type = "string",
456 .description =
457 "Optional JSON report path for automation",
458 .required = false}},
459 .examples =
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"}});
466
467 Register(
468 {.name = "dungeon-import-custom-collision-json",
469 .category = "dungeon",
470 .description = "Import custom collision maps from JSON",
471 .detailed_help =
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.",
477 .arguments =
478 {{.name = "in",
479 .type = "string",
480 .description = "Input JSON path",
481 .required = true},
482 {.name = "replace-all",
483 .type = "boolean",
484 .description =
485 "Clear custom collision for rooms not in the JSON file",
486 .required = false},
487 {.name = "force",
488 .type = "boolean",
489 .description =
490 "Required with --replace-all in write mode (safety gate)",
491 .required = false},
492 {.name = "dry-run",
493 .type = "boolean",
494 .description = "Validate and summarize without writing ROM data",
495 .required = false},
496 {.name = "report",
497 .type = "string",
498 .description = "Optional JSON report path for automation",
499 .required = false}},
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"}});
508
509 Register(
510 {.name = "dungeon-export-water-fill-json",
511 .category = "dungeon",
512 .description = "Export water fill zones to JSON",
513 .detailed_help =
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",
518 .type = "string",
519 .description = "Output JSON path",
520 .required = true},
521 {.name = "room",
522 .type = "number",
523 .description = "Single room ID (hex)",
524 .required = false},
525 {.name = "rooms",
526 .type = "string",
527 .description = "Comma-separated room IDs (hex)",
528 .required = false},
529 {.name = "all",
530 .type = "boolean",
531 .description = "Export all dungeon rooms (0-319)",
532 .required = false},
533 {.name = "report",
534 .type = "string",
535 .description =
536 "Optional JSON report path for automation",
537 .required = false}},
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"}});
543
544 Register(
545 {.name = "dungeon-import-water-fill-json",
546 .category = "dungeon",
547 .description = "Import water fill zones from JSON",
548 .detailed_help =
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",
555 .type = "string",
556 .description = "Input JSON path",
557 .required = true},
558 {.name = "dry-run",
559 .type = "boolean",
560 .description =
561 "Validate and summarize without writing ROM data",
562 .required = false},
563 {.name = "strict-masks",
564 .type = "boolean",
565 .description = "Fail if SRAM masks require "
566 "normalization (fail-closed mode)",
567 .required = false},
568 {.name = "report",
569 .type = "string",
570 .description =
571 "Optional JSON report path for automation",
572 .required = false}},
573 .examples =
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 "
577 "--strict-masks"},
578 .related_tools = {"dungeon-export-water-fill-json", "rom-doctor"}});
579
580 Register(
581 {.name = "dungeon-minecart-audit",
582 .category = "dungeon",
583 .description = "Audit minecart-related room data",
584 .detailed_help =
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.",
588 .arguments =
589 {{.name = "room",
590 .type = "number",
591 .description =
592 "Room ID (0-319). Mutually exclusive with rooms/all",
593 .required = false},
594 {.name = "rooms",
595 .type = "string",
596 .description =
597 "Comma-separated room list (hex), e.g. 0x77,0xA8,0xB8",
598 .required = false},
599 {.name = "all",
600 .type = "boolean",
601 .description = "If true, audit all rooms (0-319)",
602 .required = false},
603 {.name = "only-issues",
604 .type = "boolean",
605 .description = "If true, emit only rooms with issues",
606 .required = false},
607 {.name = "only-matches",
608 .type = "boolean",
609 .description =
610 "If true, emit only rooms with minecart-related data",
611 .required = false},
612 {.name = "include-track-objects",
613 .type = "boolean",
614 .description =
615 "If true, treat track objects as a match even if collision "
616 "data is absent (useful when collision work is unfinished)",
617 .required = false},
618 {.name = "track-object-id",
619 .type = "number",
620 .description = "Track object ID (default: 0x31)",
621 .required = false},
622 {.name = "minecart-sprite-id",
623 .type = "number",
624 .description = "Minecart sprite ID (default: 0xA3)",
625 .required = false}},
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"}});
630
631 // Overworld tools
632 Register(
633 {.name = "overworld-describe-map",
634 .category = "overworld",
635 .description = "Get detailed description of an overworld map",
636 .arguments = {{.name = "map",
637 .type = "number",
638 .description = "Map ID (0-159)",
639 .required = true}},
640 .examples = {"z3ed overworld-describe-map --map=0"},
641 .related_tools = {"overworld-list-sprites", "overworld-find-tile"}});
642
643 // Filesystem tools
644 Register({.name = "filesystem-list",
645 .category = "filesystem",
646 .description = "List directory contents",
647 .arguments = {{.name = "path",
648 .type = "string",
649 .description = "Directory path to list",
650 .required = true}},
651 .examples = {"z3ed filesystem-list --path=src/app"},
652 .requires_rom = false,
653 .related_tools = {"filesystem-read", "filesystem-exists"}});
654
655 Register({.name = "filesystem-read",
656 .category = "filesystem",
657 .description = "Read file contents",
658 .arguments = {{.name = "path",
659 .type = "string",
660 .description = "File path to read",
661 .required = true},
662 {.name = "lines",
663 .type = "number",
664 .description = "Maximum lines to read",
665 .required = false,
666 .default_value = "100"}},
667 .examples = {"z3ed filesystem-read --path=src/app/rom.h"},
668 .requires_rom = false});
669
670 // Memory tools
671 Register({.name = "memory-regions",
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.",
677 .arguments = {},
678 .examples = {"z3ed memory-regions"},
679 .requires_rom = false,
680 .related_tools = {"memory-analyze", "memory-search"}});
681
682 // Test helper tools
683 Register({.name = "tools-harness-state",
684 .category = "tools",
685 .description = "Generate WRAM state for test harnesses",
686 .detailed_help =
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",
691 .type = "string",
692 .description = "Path to ROM file",
693 .required = true},
694 {.name = "output",
695 .type = "string",
696 .description = "Output header file path",
697 .required = true}},
698 .examples = {"z3ed tools-harness-state --rom=zelda3.sfc "
699 "--output=harness_state.h"},
700 .requires_rom = false});
701
702 Register({.name = "tools-extract-golden",
703 .category = "tools",
704 .description = "Extract comprehensive golden data from ROM",
705 .arguments = {{.name = "rom",
706 .type = "string",
707 .description = "Path to ROM file",
708 .required = true},
709 {.name = "output",
710 .type = "string",
711 .description = "Output header file path",
712 .required = true}},
713 .examples = {"z3ed tools-extract-golden --rom=zelda3.sfc "
714 "--output=golden_data.h"}});
715
716 // Visual analysis tools
717 Register({.name = "visual-find-similar-tiles",
718 .category = "visual",
719 .description = "Find tiles with similar patterns to a reference",
720 .detailed_help =
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 "
724 "ROM optimization.",
725 .arguments = {{.name = "tile_id",
726 .type = "number",
727 .description = "Reference tile ID to compare",
728 .required = true},
729 {.name = "sheet",
730 .type = "number",
731 .description = "Graphics sheet index (0-222)",
732 .required = false,
733 .default_value = "0"},
734 {.name = "threshold",
735 .type = "number",
736 .description = "Minimum similarity score (0-100)",
737 .required = false,
738 .default_value = "80"},
739 {.name = "method",
740 .type = "string",
741 .description = "Comparison method",
742 .required = false,
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"}});
750
751 Register({.name = "visual-analyze-spritesheet",
752 .category = "visual",
753 .description = "Identify unused regions in graphics sheets",
754 .detailed_help =
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",
759 .type = "number",
760 .description =
761 "Specific sheet to analyze (all if omitted)",
762 .required = false},
763 {.name = "tile_size",
764 .type = "number",
765 .description = "Tile size to check (8 or 16)",
766 .required = false,
767 .default_value = "8",
768 .enum_values = {"8", "16"}}},
769 .examples = {"z3ed visual-analyze-spritesheet",
770 "z3ed visual-analyze-spritesheet --sheet=10 "
771 "--tile_size=16"},
772 .related_tools = {"visual-find-similar-tiles"}});
773
774 Register({.name = "visual-palette-usage",
775 .category = "visual",
776 .description = "Analyze palette usage statistics across maps",
777 .detailed_help =
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",
782 .type = "string",
783 .description = "Map type to analyze",
784 .required = false,
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"}});
790
791 Register(
792 {.name = "visual-tile-histogram",
793 .category = "visual",
794 .description = "Generate frequency histogram of tile usage",
795 .detailed_help =
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 "
799 "for replacement.",
800 .arguments = {{.name = "type",
801 .type = "string",
802 .description = "Map type to analyze",
803 .required = false,
804 .default_value = "overworld",
805 .enum_values = {"overworld", "dungeon"}},
806 {.name = "top",
807 .type = "number",
808 .description = "Number of top entries to return",
809 .required = false,
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"}});
815
816 // =========================================================================
817 // Code Generation Tools
818 // =========================================================================
819
820 Register(
821 {.name = "codegen-asm-hook",
822 .category = "codegen",
823 .description = "Generate ASM hook at ROM address",
824 .detailed_help =
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",
829 .type = "hex",
830 .description = "ROM address to hook (hex)",
831 .required = true},
832 {.name = "label",
833 .type = "string",
834 .description = "Label name for the hook",
835 .required = true},
836 {.name = "nop-fill",
837 .type = "number",
838 .description = "Number of NOP bytes to add",
839 .required = false,
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"}});
846
847 Register(
848 {.name = "codegen-freespace-patch",
849 .category = "codegen",
850 .description = "Generate patch using detected free regions",
851 .detailed_help =
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",
856 .type = "string",
857 .description = "Label for the code block",
858 .required = true},
859 {.name = "size",
860 .type = "hex",
861 .description = "Size in bytes needed (hex)",
862 .required = true},
863 {.name = "prefer-bank",
864 .type = "hex",
865 .description = "Preferred bank number (hex)",
866 .required = false}},
867 .examples =
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"}});
873
874 Register({.name = "codegen-sprite-template",
875 .category = "codegen",
876 .description = "Generate sprite ASM from template",
877 .detailed_help =
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",
882 .type = "string",
883 .description = "Sprite name/label",
884 .required = true},
885 {.name = "init-code",
886 .type = "string",
887 .description = "Initialization ASM code",
888 .required = false},
889 {.name = "main-code",
890 .type = "string",
891 .description = "Main loop ASM code",
892 .required = false}},
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"}});
898
899 Register(
900 {.name = "codegen-event-handler",
901 .category = "codegen",
902 .description = "Generate event handler code",
903 .detailed_help =
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",
908 .type = "string",
909 .description = "Event type",
910 .required = true,
911 .enum_values = {"nmi", "irq", "reset"}},
912 {.name = "label",
913 .type = "string",
914 .description = "Handler label name",
915 .required = true},
916 {.name = "custom-code",
917 .type = "string",
918 .description = "Custom ASM code",
919 .required = false}},
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"}});
925
926 // =========================================================================
927 // Project Management Tools
928 // =========================================================================
929
930 Register({.name = "project-status",
931 .category = "project",
932 .description = "Show current project state and pending edits",
933 .detailed_help =
934 "Displays current project state including loaded ROM info, "
935 "pending uncommitted edits, available snapshots, and ROM "
936 "checksum for validation.",
937 .arguments = {},
938 .examples = {"z3ed project-status"},
939 .requires_rom = true,
940 .related_tools = {"project-snapshot", "project-restore"}});
941
942 Register({.name = "project-snapshot",
943 .category = "project",
944 .description = "Create named checkpoint with edit deltas",
945 .detailed_help =
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",
950 .type = "string",
951 .description = "Snapshot name",
952 .required = true},
953 {.name = "description",
954 .type = "string",
955 .description = "Optional description",
956 .required = false}},
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",
962 "project-diff"}});
963
964 Register({.name = "project-restore",
965 .category = "project",
966 .description = "Restore ROM to named checkpoint",
967 .detailed_help =
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",
972 .type = "string",
973 .description = "Snapshot name to restore",
974 .required = true}},
975 .examples = {"z3ed project-restore --name=before-edit"},
976 .requires_rom = true,
977 .related_tools = {"project-snapshot", "project-status"}});
978
979 Register(
980 {.name = "project-export",
981 .category = "project",
982 .description = "Export project as portable archive",
983 .detailed_help =
984 "Exports the project metadata and all snapshots as a "
985 "portable archive file. Optionally includes the base ROM.",
986 .arguments = {{.name = "path",
987 .type = "string",
988 .description = "Output file path",
989 .required = true},
990 {.name = "include-rom",
991 .type = "flag",
992 .description = "Include base ROM in export",
993 .required = false}},
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"}});
998
999 Register({.name = "project-import",
1000 .category = "project",
1001 .description = "Import project archive",
1002 .detailed_help =
1003 "Imports a project archive and loads its metadata and "
1004 "snapshots. Validates project structure and checksums.",
1005 .arguments = {{.name = "path",
1006 .type = "string",
1007 .description = "Archive file path",
1008 .required = true}},
1009 .examples = {"z3ed project-import --path=myproject.tar.gz"},
1010 .requires_rom = false,
1011 .related_tools = {"project-export"}});
1012
1013 Register(
1014 {.name = "project-diff",
1015 .category = "project",
1016 .description = "Compare two project states",
1017 .detailed_help =
1018 "Compares two snapshots and shows the differences in edits "
1019 "between them. Useful for reviewing changes between versions.",
1020 .arguments = {{.name = "snapshot1",
1021 .type = "string",
1022 .description = "First snapshot name",
1023 .required = true},
1024 {.name = "snapshot2",
1025 .type = "string",
1026 .description = "Second snapshot name",
1027 .required = true}},
1028 .examples = {"z3ed project-diff --snapshot1=v1 --snapshot2=v2"},
1029 .requires_rom = true,
1030 .related_tools = {"project-snapshot", "rom-diff"}});
1031
1032 // =========================================================================
1033 // Mesen2 Debugging Tools (Live Emulator Integration)
1034 // =========================================================================
1035
1036 Register({.name = "mesen-gamestate",
1037 .category = "mesen2",
1038 .description = "Get ALTTP game state from running Mesen2",
1039 .detailed_help =
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.",
1043 .arguments = {},
1044 .examples = {"z3ed mesen-gamestate"},
1045 .requires_rom = false,
1046 .requires_grpc = true,
1047 .related_tools = {"mesen-sprites", "mesen-cpu"}});
1048
1049 Register({.name = "mesen-sprites",
1050 .category = "mesen2",
1051 .description = "Get active sprites from running Mesen2",
1052 .detailed_help =
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",
1057 .type = "boolean",
1058 .description = "Include inactive sprite slots",
1059 .required = false,
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"}});
1065
1066 Register({.name = "mesen-cpu",
1067 .category = "mesen2",
1068 .description = "Get CPU register state from Mesen2",
1069 .detailed_help =
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.",
1073 .arguments = {},
1074 .examples = {"z3ed mesen-cpu"},
1075 .requires_rom = false,
1076 .requires_grpc = true,
1077 .related_tools = {"mesen-gamestate", "mesen-disasm"}});
1078
1079 Register(
1080 {.name = "mesen-memory-read",
1081 .category = "mesen2",
1082 .description = "Read memory from Mesen2 emulator",
1083 .detailed_help =
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",
1088 .type = "hex",
1089 .description = "Start address (hex)",
1090 .required = true},
1091 {.name = "length",
1092 .type = "number",
1093 .description = "Number of bytes to read",
1094 .required = false,
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"}});
1100
1101 Register(
1102 {.name = "mesen-memory-write",
1103 .category = "mesen2",
1104 .description = "Write memory in Mesen2 emulator",
1105 .detailed_help =
1106 "Writes bytes to memory in the running Mesen2 instance. "
1107 "Useful for testing ROM patches or modifying game state.",
1108 .arguments = {{.name = "address",
1109 .type = "hex",
1110 .description = "Target address (hex)",
1111 .required = true},
1112 {.name = "data",
1113 .type = "string",
1114 .description = "Hex bytes to write",
1115 .required = true}},
1116 .examples = {"z3ed mesen-memory-write --address=0x7EF36D --data=A0"},
1117 .requires_rom = false,
1118 .requires_grpc = true,
1119 .related_tools = {"mesen-memory-read"}});
1120
1121 Register({.name = "mesen-disasm",
1122 .category = "mesen2",
1123 .description = "Disassemble code at address in Mesen2",
1124 .detailed_help =
1125 "Disassembles instructions at the specified address using "
1126 "Mesen2's built-in disassembler, which uses loaded symbols "
1127 "for labels.",
1128 .arguments = {{.name = "address",
1129 .type = "hex",
1130 .description = "Start address (hex)",
1131 .required = true},
1132 {.name = "count",
1133 .type = "number",
1134 .description = "Number of instructions",
1135 .required = false,
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"}});
1141
1142 Register({.name = "mesen-trace",
1143 .category = "mesen2",
1144 .description = "Get execution trace from Mesen2",
1145 .detailed_help =
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",
1150 .type = "number",
1151 .description = "Number of trace entries",
1152 .required = false,
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"}});
1158
1159 Register(
1160 {.name = "mesen-breakpoint",
1161 .category = "mesen2",
1162 .description = "Manage breakpoints in Mesen2",
1163 .detailed_help =
1164 "Add, remove, or list breakpoints in the running Mesen2 "
1165 "instance. Supports execution, read, and write breakpoints.",
1166 .arguments = {{.name = "action",
1167 .type = "string",
1168 .description = "Breakpoint action",
1169 .required = true,
1170 .enum_values = {"add", "remove", "clear", "list"}},
1171 {.name = "address",
1172 .type = "hex",
1173 .description = "Address for add/remove",
1174 .required = false},
1175 {.name = "type",
1176 .type = "string",
1177 .description = "Breakpoint type",
1178 .required = false,
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"}});
1186
1187 Register({.name = "mesen-control",
1188 .category = "mesen2",
1189 .description = "Control Mesen2 emulation state",
1190 .detailed_help =
1191 "Pause, resume, step, or frame advance the running Mesen2 "
1192 "instance. For automated testing and debugging workflows.",
1193 .arguments = {{.name = "action",
1194 .type = "string",
1195 .description = "Control action",
1196 .required = true,
1197 .enum_values = {"pause", "resume", "step", "frame",
1198 "reset"}}},
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"}});
1204
1205 Register(
1206 {.name = "mesen-session",
1207 .category = "mesen2",
1208 .description = "Read tracked autonomous emulator session state",
1209 .detailed_help =
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",
1214 .type = "string",
1215 .description =
1216 "Session action (default: show). export/import "
1217 "require --file",
1218 .required = false,
1219 .enum_values = {"show", "reset", "export", "import"}},
1220 {.name = "file",
1221 .type = "string",
1222 .description =
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"}});
1230
1231 Register(
1232 {.name = "mesen-await",
1233 .category = "mesen2",
1234 .description = "Block until frame/pc/breakpoint condition is met",
1235 .detailed_help =
1236 "Polling wait primitive for deterministic automation. Waits until "
1237 "a frame delta is reached, CPU PC matches an address, or a "
1238 "tracked "
1239 "breakpoint ID is hit.",
1240 .arguments = {{.name = "type",
1241 .type = "string",
1242 .description = "Await condition type",
1243 .required = true,
1244 .enum_values = {"frame", "pc", "breakpoint"}},
1245 {.name = "count",
1246 .type = "number",
1247 .description = "Frame delta when --type=frame",
1248 .required = false},
1249 {.name = "address",
1250 .type = "hex",
1251 .description = "Target PC when --type=pc",
1252 .required = false},
1253 {.name = "id",
1254 .type = "number",
1255 .description = "Breakpoint ID when --type=breakpoint",
1256 .required = false},
1257 {.name = "timeout-ms",
1258 .type = "number",
1259 .description = "Timeout in milliseconds",
1260 .required = false,
1261 .default_value = "2000"},
1262 {.name = "poll-ms",
1263 .type = "number",
1264 .description = "Polling interval in milliseconds",
1265 .required = false,
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"}});
1272
1273 Register(
1274 {.name = "mesen-goal",
1275 .category = "mesen2",
1276 .description = "Execute deterministic emulator control macros",
1277 .detailed_help =
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",
1283 .type = "string",
1284 .description = "Goal macro name",
1285 .required = true,
1286 .enum_values = {"break-at", "run-frames",
1287 "capture-state-at-pc"}},
1288 {.name = "address",
1289 .type = "hex",
1290 .description =
1291 "Target PC for break-at/capture-state-at-pc",
1292 .required = false},
1293 {.name = "count",
1294 .type = "number",
1295 .description = "Frame count for run-frames",
1296 .required = false},
1297 {.name = "timeout-ms",
1298 .type = "number",
1299 .description = "Timeout in milliseconds",
1300 .required = false,
1301 .default_value = "2000"},
1302 {.name = "poll-ms",
1303 .type = "number",
1304 .description = "Polling interval in milliseconds",
1305 .required = false,
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",
1314 "mesen-control"}});
1315
1316 Register(
1317 {.name = "mesen-state-verify",
1318 .category = "mesen2",
1319 .description =
1320 "Verify savestate freshness against ROM hash and metadata",
1321 .detailed_help =
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.",
1325 .arguments =
1326 {{.name = "state",
1327 .type = "string",
1328 .description = "Savestate file path",
1329 .required = true},
1330 {.name = "rom-file",
1331 .type = "string",
1332 .description = "ROM file path to validate against",
1333 .required = true},
1334 {.name = "meta",
1335 .type = "string",
1336 .description =
1337 "Optional metadata path (default: <state>.meta.json)",
1338 .required = false},
1339 {.name = "scenario",
1340 .type = "string",
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",
1350 "mesen-goal"}});
1351
1352 Register(
1353 {.name = "mesen-state-regen",
1354 .category = "mesen2",
1355 .description = "Regenerate savestate metadata for freshness checks",
1356 .detailed_help =
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",
1361 .type = "string",
1362 .description = "Savestate file path",
1363 .required = true},
1364 {.name = "rom-file",
1365 .type = "string",
1366 .description = "ROM file path",
1367 .required = true},
1368 {.name = "meta",
1369 .type = "string",
1370 .description = "Optional output metadata path "
1371 "(default: <state>.meta.json)",
1372 .required = false},
1373 {.name = "scenario",
1374 .type = "string",
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",
1384 "mesen-goal"}});
1385
1386 Register(
1387 {.name = "mesen-state-capture",
1388 .category = "mesen2",
1389 .description = "Capture savestate metadata in one command",
1390 .detailed_help =
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.",
1394 .arguments =
1395 {{.name = "state",
1396 .type = "string",
1397 .description = "Target savestate path",
1398 .required = true},
1399 {.name = "rom-file",
1400 .type = "string",
1401 .description = "ROM file path",
1402 .required = true},
1403 {.name = "meta",
1404 .type = "string",
1405 .description = "Optional output metadata path "
1406 "(default: <state>.meta.json)",
1407 .required = false},
1408 {.name = "scenario",
1409 .type = "string",
1410 .description = "Optional scenario ID to store",
1411 .required = false},
1412 {.name = "slot",
1413 .type = "number",
1414 .description = "Optional Mesen save slot to trigger before "
1415 "metadata refresh",
1416 .required = false},
1417 {.name = "states-dir",
1418 .type = "string",
1419 .description = "Optional directory to auto-locate latest "
1420 ".state when target path is missing",
1421 .required = false},
1422 {.name = "wait-ms",
1423 .type = "number",
1424 .description = "Delay after slot save before file capture",
1425 .required = false,
1426 .default_value = "400"}},
1427 .examples =
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",
1436 "mesen-session"}});
1437
1438 Register(
1439 {.name = "mesen-state-hook",
1440 .category = "mesen2",
1441 .description = "Agent preflight hook for savestate freshness",
1442 .detailed_help =
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.",
1446 .arguments =
1447 {{.name = "state",
1448 .type = "string",
1449 .description = "Savestate file path",
1450 .required = true},
1451 {.name = "rom-file",
1452 .type = "string",
1453 .description = "ROM file path",
1454 .required = true},
1455 {.name = "meta",
1456 .type = "string",
1457 .description =
1458 "Optional metadata path (default: <state>.meta.json)",
1459 .required = false},
1460 {.name = "scenario",
1461 .type = "string",
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"}});
1471 }
1472
1473 std::map<std::string, ToolSchema> schemas_;
1474};
1475
1476} // namespace agent
1477} // namespace cli
1478} // namespace yaze
1479
1480#endif // YAZE_SRC_CLI_SERVICE_AGENT_TOOL_SCHEMAS_H_
Registry of all tool schemas.
std::map< std::string, ToolSchema > schemas_
std::vector< ToolSchema > GetByCategory(const std::string &category) const
std::string ExportAllAsJson() const
Export all schemas as JSON array for function calling APIs.
std::string GenerateLLMPrompt() const
Generate LLM system prompt section for tools.
std::vector< ToolSchema > GetAll() const
static ToolSchemaRegistry & Instance()
void Register(const ToolSchema &schema)
std::string ExportAllAsMarkdown() const
Export all schemas as Markdown documentation.
const ToolSchema * Get(const std::string &name) const
Get detailed information about a tool
Argument schema for a tool parameter.
std::vector< std::string > enum_values
Complete tool schema for LLM documentation.
std::vector< ArgumentSchema > arguments
std::string ToJson() const
Generate JSON schema for function calling APIs.
std::vector< std::string > examples
std::vector< std::string > related_tools
std::string ToMarkdown() const
Generate markdown documentation for a tool.