yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
command_registry.cc
Go to the documentation of this file.
2
3#include "absl/strings/str_cat.h"
4#include "absl/strings/str_format.h"
5#include "absl/strings/str_join.h"
7
8namespace yaze {
9namespace cli {
10
12 static CommandRegistry instance;
13 static bool initialized = false;
14 if (!initialized) {
15 instance.RegisterAllCommands();
16 initialized = true;
17 }
18 return instance;
19}
20
21void CommandRegistry::Register(std::unique_ptr<resources::CommandHandler> handler,
22 const CommandMetadata& metadata) {
23 std::string name = handler->GetName();
24
25 // Store metadata
26 metadata_[name] = metadata;
27
28 // Register aliases
29 for (const auto& alias : metadata.aliases) {
30 aliases_[alias] = name;
31 }
32
33 // Store handler
34 handlers_[name] = std::move(handler);
35}
36
37resources::CommandHandler* CommandRegistry::Get(const std::string& name) const {
38 // Check direct name
39 auto it = handlers_.find(name);
40 if (it != handlers_.end()) {
41 return it->second.get();
42 }
43
44 // Check aliases
45 auto alias_it = aliases_.find(name);
46 if (alias_it != aliases_.end()) {
47 auto handler_it = handlers_.find(alias_it->second);
48 if (handler_it != handlers_.end()) {
49 return handler_it->second.get();
50 }
51 }
52
53 return nullptr;
54}
55
57 const std::string& name) const {
58 // Resolve alias first
59 std::string canonical_name = name;
60 auto alias_it = aliases_.find(name);
61 if (alias_it != aliases_.end()) {
62 canonical_name = alias_it->second;
63 }
64
65 auto it = metadata_.find(canonical_name);
66 return (it != metadata_.end()) ? &it->second : nullptr;
67}
68
70 const std::string& category) const {
71 std::vector<std::string> result;
72 for (const auto& [name, metadata] : metadata_) {
73 if (metadata.category == category) {
74 result.push_back(name);
75 }
76 }
77 return result;
78}
79
80std::vector<std::string> CommandRegistry::GetCategories() const {
81 std::vector<std::string> categories;
82 for (const auto& [_, metadata] : metadata_) {
83 if (std::find(categories.begin(), categories.end(), metadata.category) == categories.end()) {
84 categories.push_back(metadata.category);
85 }
86 }
87 return categories;
88}
89
90std::vector<std::string> CommandRegistry::GetAgentCommands() const {
91 std::vector<std::string> result;
92 for (const auto& [name, metadata] : metadata_) {
93 if (metadata.available_to_agent) {
94 result.push_back(name);
95 }
96 }
97 return result;
98}
99
101 // TODO: Generate JSON function schemas from metadata
102 // This would replace manual function_schemas.json maintenance
103 return "{}"; // Placeholder
104}
105
106std::string CommandRegistry::GenerateHelp(const std::string& name) const {
107 auto* metadata = GetMetadata(name);
108 if (!metadata) {
109 return absl::StrFormat("Command '%s' not found", name);
110 }
111
112 std::ostringstream help;
113 help << "\n\033[1;36m" << metadata->name << "\033[0m - " << metadata->description << "\n\n";
114 help << "\033[1;33mUsage:\033[0m\n";
115 help << " " << metadata->usage << "\n\n";
116
117 if (!metadata->examples.empty()) {
118 help << "\033[1;33mExamples:\033[0m\n";
119 for (const auto& example : metadata->examples) {
120 help << " " << example << "\n";
121 }
122 help << "\n";
123 }
124
125 if (metadata->requires_rom) {
126 help << "\033[1;33mRequires:\033[0m ROM file (--rom=<path>)\n";
127 }
128 if (metadata->requires_grpc) {
129 help << "\033[1;33mRequires:\033[0m YAZE running with gRPC enabled\n";
130 }
131
132 if (!metadata->aliases.empty()) {
133 help << "\n\033[1;33mAliases:\033[0m " << absl::StrJoin(metadata->aliases, ", ") << "\n";
134 }
135
136 return help.str();
137}
138
139std::string CommandRegistry::GenerateCategoryHelp(const std::string& category) const {
140 auto commands = GetCommandsInCategory(category);
141 if (commands.empty()) {
142 return absl::StrFormat("No commands in category '%s'", category);
143 }
144
145 std::ostringstream help;
146 help << "\n\033[1;36m" << category << " commands:\033[0m\n\n";
147
148 for (const auto& cmd : commands) {
149 auto* metadata = GetMetadata(cmd);
150 if (metadata) {
151 help << " \033[1;33m" << cmd << "\033[0m\n";
152 help << " " << metadata->description << "\n";
153 if (!metadata->usage.empty()) {
154 help << " Usage: " << metadata->usage << "\n";
155 }
156 help << "\n";
157 }
158 }
159
160 return help.str();
161}
162
164 std::ostringstream help;
165 help << "\n\033[1;36mAll z3ed Commands:\033[0m\n\n";
166
167 auto categories = GetCategories();
168 for (const auto& category : categories) {
169 help << GenerateCategoryHelp(category);
170 }
171
172 return help.str();
173}
174
175absl::Status CommandRegistry::Execute(const std::string& name,
176 const std::vector<std::string>& args,
177 Rom* rom_context) {
178 auto* handler = Get(name);
179 if (!handler) {
180 return absl::NotFoundError(absl::StrFormat("Command '%s' not found", name));
181 }
182
183 return handler->Run(args, rom_context);
184}
185
186bool CommandRegistry::HasCommand(const std::string& name) const {
187 return Get(name) != nullptr;
188}
189
191 // Get all command handlers from factory
192 auto all_handlers = handlers::CreateAllCommandHandlers();
193
194 for (auto& handler : all_handlers) {
195 std::string name = handler->GetName();
196
197 // Build metadata from handler
198 CommandMetadata metadata;
199 metadata.name = name;
200 metadata.usage = handler->GetUsage();
201 metadata.available_to_agent = true; // Most commands available to agent
202 metadata.requires_rom = true; // Most commands need ROM
203 metadata.requires_grpc = false;
204
205 // Categorize and enhance metadata based on command type
206 if (name.find("resource-") == 0) {
207 metadata.category = "resource";
208 metadata.description = "Resource inspection and search";
209 if (name == "resource-list") {
210 metadata.examples = {
211 "z3ed resource-list --type=dungeon --format=json",
212 "z3ed resource-list --type=sprite --format=table"
213 };
214 }
215 } else if (name.find("dungeon-") == 0) {
216 metadata.category = "dungeon";
217 metadata.description = "Dungeon inspection and editing";
218 if (name == "dungeon-describe-room") {
219 metadata.examples = {
220 "z3ed dungeon-describe-room --room=5 --format=json"
221 };
222 }
223 } else if (name.find("overworld-") == 0) {
224 metadata.category = "overworld";
225 metadata.description = "Overworld inspection and editing";
226 if (name == "overworld-find-tile") {
227 metadata.examples = {
228 "z3ed overworld-find-tile --tile=0x42 --format=json"
229 };
230 }
231 } else if (name.find("emulator-") == 0) {
232 metadata.category = "emulator";
233 metadata.description = "Emulator control and debugging";
234 metadata.requires_grpc = true;
235 if (name == "emulator-set-breakpoint") {
236 metadata.examples = {
237 "z3ed emulator-set-breakpoint --address=0x83D7 --description='NMI handler'"
238 };
239 }
240 } else if (name.find("gui-") == 0) {
241 metadata.category = "gui";
242 metadata.description = "GUI automation";
243 metadata.requires_grpc = true;
244 } else if (name.find("hex-") == 0) {
245 metadata.category = "graphics";
246 metadata.description = "Hex data manipulation";
247 } else if (name.find("palette-") == 0) {
248 metadata.category = "graphics";
249 metadata.description = "Palette operations";
250 } else if (name.find("sprite-") == 0) {
251 metadata.category = "graphics";
252 metadata.description = "Sprite operations";
253 } else if (name.find("message-") == 0 || name.find("dialogue-") == 0) {
254 metadata.category = "game";
255 metadata.description = name.find("message-") == 0 ? "Message inspection" : "Dialogue inspection";
256 } else if (name.find("music-") == 0) {
257 metadata.category = "game";
258 metadata.description = "Music/audio inspection";
259 } else if (name == "simple-chat" || name == "chat") {
260 metadata.category = "agent";
261 metadata.description = "AI conversational agent";
262 metadata.available_to_agent = false; // Meta-command
263 metadata.requires_rom = false;
264 metadata.examples = {
265 "z3ed simple-chat --rom=zelda3.sfc",
266 "z3ed simple-chat \"What dungeons exist?\" --rom=zelda3.sfc"
267 };
268 } else {
269 metadata.category = "misc";
270 metadata.description = "Miscellaneous command";
271 }
272
273 Register(std::move(handler), metadata);
274 }
275}
276
277} // namespace cli
278} // namespace yaze
279
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:74
Single source of truth for all z3ed commands.
std::map< std::string, std::string > aliases_
std::vector< std::string > GetCommandsInCategory(const std::string &category) const
Get all commands in a category.
static CommandRegistry & Instance()
std::string GenerateCategoryHelp(const std::string &category) const
Generate category help text.
std::string ExportFunctionSchemas() const
Export function schemas for AI tool calling (JSON)
const CommandMetadata * GetMetadata(const std::string &name) const
Get command metadata.
absl::Status Execute(const std::string &name, const std::vector< std::string > &args, Rom *rom_context=nullptr)
Execute a command by name.
std::string GenerateCompleteHelp() const
Generate complete help text (all commands)
std::vector< std::string > GetAgentCommands() const
Get all commands available to AI agents.
std::map< std::string, CommandMetadata > metadata_
std::map< std::string, std::unique_ptr< resources::CommandHandler > > handlers_
resources::CommandHandler * Get(const std::string &name) const
Get a command handler by name or alias.
bool HasCommand(const std::string &name) const
Check if command exists.
std::vector< std::string > GetCategories() const
Get all categories.
void Register(std::unique_ptr< resources::CommandHandler > handler, const CommandMetadata &metadata)
Register a command handler.
std::string GenerateHelp(const std::string &name) const
Generate help text for a command.
Base class for CLI command handlers.
std::vector< std::unique_ptr< resources::CommandHandler > > CreateAllCommandHandlers()
Factory function to create all command handlers (CLI + agent)
Main namespace for the application.
Definition controller.cc:20