6#include "absl/status/statusor.h"
7#include "absl/strings/str_cat.h"
19 absl::string_view title;
20 absl::StatusOr<CommandInvocation> (*resolve)(absl::Span<const std::string>);
21 std::string (*usage)();
32 if (group.name == name) {
40 return token ==
"--help" || token ==
"-h";
44 " z3ed [command] [flags]\n"
45 " z3ed help <command|category> # Scoped help\n"
46 " z3ed --export-schemas # JSON schemas for agents\n"
47 " z3ed --version # Show version\n";
50 " --rom=<path> Path to ROM file\n"
51 " --sandbox Run ROM commands in a sandbox copy\n"
52 " --quiet, -q Suppress output\n"
53 " --ai_provider=<name> AI provider (auto, ollama, gemini, openai,\n"
55 " --ai_model=<name> Provider-specific model override\n"
56 " --version Show version\n"
57 " --self-test Run self-test diagnostics\n"
58 " --help <scope> Show help for command or category\n"
59 " --export-schemas Export command schemas as JSON\n";
62 " --http-port=<port> HTTP API server port (0=disabled)\n"
63 " (no command keeps server alive until Ctrl+C)\n"
64 " --http-host=<host> HTTP API bind address (default localhost)\n"
65 " --http-allow-commands Serve /api/v1/command/execute (runs any "
69 " z3ed rom-info --rom=zelda3.sfc\n"
70 " z3ed rom read --address=0x1000 --length=16 --rom=zelda3.sfc\n"
72 " z3ed message-search --rom=zelda3.sfc --query=\"Master Sword\"\n"
73 " z3ed dungeon-export --rom=zelda3.sfc --id=1\n";
78 : registry_(registry), options_(std::move(options)), out_(out) {}
80absl::Status
Cli::Run(absl::Span<const std::string> args)
const {
83 return absl::OkStatus();
86 const std::string& command = args[0];
87 const absl::Span<const std::string> rest = args.subspan(1);
89 if (command ==
"help") {
92 return absl::OkStatus();
96 if (command ==
"agent") {
99 if (FindAliasGroup(command) !=
nullptr) {
104 command, std::vector<std::string>(rest.begin(), rest.end()),
nullptr);
108 return absl::OkStatus();
110 return absl::NotFoundError(absl::StrCat(
"Unknown command: ", command));
114 if (args.empty() || IsHelpFlag(args[0])) {
116 return absl::OkStatus();
119 return absl::FailedPreconditionError(
"Agent CLI is disabled in this build");
122 std::vector<std::string>(args.begin(), args.end()));
126 absl::Span<const std::string> args)
const {
127 if (args.empty() || IsHelpFlag(args[0])) {
129 return absl::OkStatus();
132 const AliasGroup& group = *FindAliasGroup(group_name);
133 absl::StatusOr<CommandInvocation> invocation = group.resolve(args);
134 if (!invocation.ok()) {
135 if (absl::IsInvalidArgument(invocation.status())) {
138 return invocation.status();
141 return absl::NotFoundError(
142 absl::StrCat(
"Command not found for ", group.name,
143 " subcommand: ", invocation->command));
153 <<
"Yet Another Zelda3 Editor - AI-Powered CLI" << style.
Reset()
157 << kUsageText <<
"\n";
169 out_ << kHttpFlagsText;
171 out_ <<
" --mesen-socket=<path> Override Mesen2 socket path\n\n";
175 out_ <<
" z3ed agent simple-chat --rom=zelda3.sfc\n";
177 out_ << kExamplesText <<
"\n";
179 out_ <<
"For detailed help: z3ed help <command>\n";
180 out_ <<
"For all commands: z3ed --list-commands\n\n";
186 return absl::OkStatus();
190 const std::string& target = words[0];
191 if (target ==
"all") {
193 return absl::OkStatus();
197 return absl::OkStatus();
199 if (target ==
"agent") {
205 return absl::OkStatus();
207 if (
const AliasGroup* group = FindAliasGroup(target)) {
208 if (words.size() > 1) {
209 absl::StatusOr<CommandInvocation> invocation =
210 group->resolve(words.subspan(1));
213 return absl::OkStatus();
217 return absl::OkStatus();
221 return absl::OkStatus();
223 return absl::NotFoundError(
224 absl::StrCat(
"Unknown command or category '", target,
225 "'. Use --list-commands for a full command list."));
230 out_ <<
"\n" << style.
Heading() <<
"Agent command:" << style.
Reset() <<
"\n";
232 out_ <<
" This build was compiled without agent CLI support.\n"
233 <<
" Rebuild with `YAZE_ENABLE_AGENT_CLI` to enable `z3ed agent`.\n";
236 out_ <<
" z3ed agent <subcommand> [flags]\n\n";
240 out_ <<
"Use `z3ed help agent <subcommand>` for scoped details.\n";
244 const AliasGroup& group = *FindAliasGroup(group_name);
246 const std::string category(group.name);
249 << style.
Heading() << group.title <<
":" << style.
Reset() <<
"\n"
250 << group.usage() <<
"\n";
252 out_ <<
"Equivalent direct commands:\n"
255 out_ <<
"Use `z3ed help " << group.name
256 <<
" <subcommand>` for command-specific flags.\n";
260 const std::vector<std::string> commands =
263 out_ <<
"Category: " << category <<
"\n\n";
264 if (commands.empty()) {
265 out_ <<
" No commands in this category.\n";
269 for (
const std::string& name : commands) {
272 if (metadata ==
nullptr) {
277 if (!metadata->
usage.empty()) {
278 out_ <<
" Usage: " << metadata->
usage <<
"\n";
281 std::string requirements;
283 requirements +=
"ROM ";
286 requirements +=
"gRPC ";
288 if (!requirements.empty()) {
289 out_ <<
" Requires: " << requirements <<
"\n";
298 out_ <<
"Z3ED Command Summary\n\n";
299 for (
const std::string& category : categories) {
302 out_ <<
" " << name <<
" [" << metadata->category <<
"] - "
303 << metadata->description <<
"\n";
308 << categories.size() <<
" categories\n";
void PrintCategoryHelp(const std::string &category) const
absl::Status PrintScopedHelp(absl::Span< const std::string > words) const
absl::Status Run(absl::Span< const std::string > args) const
void PrintAgentHelp() const
CommandRegistry & registry_
void PrintCommandSummary() const
void PrintOverview() const
absl::Status RunAlias(absl::string_view group, absl::Span< const std::string > args) const
absl::Status RunAgent(absl::Span< const std::string > args) const
Cli(CommandRegistry ®istry, Options options, std::ostream &out)
void PrintAliasHelp(absl::string_view group) const
Single source of truth for all z3ed commands.
std::string GenerateCompleteHelp(const util::AnsiStyle &style=util::AnsiStyle(false)) const
Generate complete help text (all commands)
std::string GenerateCategoryHelp(const std::string &category, const util::AnsiStyle &style=util::AnsiStyle(false)) const
Generate category help text.
std::vector< std::string > GetCommandsInCategory(const std::string &category) const
Get all commands in a category.
const CommandMetadata * GetMetadata(const std::string &name) const
Get command metadata.
std::string GenerateHelp(const std::string &name, const util::AnsiStyle &style=util::AnsiStyle(false)) const
Generate help text for a command.
size_t Count() const
Get total command count.
absl::Status Execute(const std::string &name, const std::vector< std::string > &args, Rom *rom_context=nullptr, std::string *captured_output=nullptr)
Execute a command by name.
bool HasCommand(const std::string &name) const
Check if command exists.
std::vector< std::string > GetCategories() const
Get all categories.
constexpr absl::string_view Emphasis() const
constexpr absl::string_view Heading() const
constexpr absl::string_view Highlight() const
constexpr absl::string_view Reset() const
const AliasGroup * FindAliasGroup(absl::string_view name)
bool IsHelpFlag(absl::string_view token)
constexpr absl::string_view kUsageText
constexpr absl::string_view kCommonFlagsText
constexpr AliasGroup kAliasGroups[]
constexpr absl::string_view kHttpFlagsText
constexpr absl::string_view kExamplesText
absl::StatusOr< CommandInvocation > ResolveRomSubcommand(absl::Span< const std::string > args)
std::string RomSubcommandUsage()
absl::StatusOr< CommandInvocation > ResolveDebugSubcommand(absl::Span< const std::string > args)
std::string RenderLogo(const util::AnsiStyle &style)
std::string DebugSubcommandUsage()
AgentHandler agent_handler