diff --git a/src/cli/cli.cc b/src/cli/cli.cc index f1fb3552..effa9ada 100644 --- a/src/cli/cli.cc +++ b/src/cli/cli.cc @@ -18,11 +18,6 @@ absl::Status HandleAgentCommand(const std::vector& args); ModernCLI::ModernCLI() { // Commands are now managed by CommandRegistry singleton - // SetupCommands() is no longer needed -} - -void ModernCLI::SetupCommands() { - // No-op: CommandRegistry handles this automatically } absl::Status ModernCLI::Run(int argc, char* argv[]) { diff --git a/src/cli/cli.h b/src/cli/cli.h index 685d87cf..cfc5a45a 100644 --- a/src/cli/cli.h +++ b/src/cli/cli.h @@ -33,7 +33,6 @@ class ModernCLI { void PrintCommandSummary() const; private: - void SetupCommands(); void ShowHelp(); void ShowCategoryHelp(const std::string& category) const; void ShowCommandSummary() const; diff --git a/src/cli/handlers/agent.cc b/src/cli/handlers/agent.cc index a905b622..5140dbce 100644 --- a/src/cli/handlers/agent.cc +++ b/src/cli/handlers/agent.cc @@ -1,6 +1,8 @@ #include "cli/handlers/agent/todo_commands.h" #include "cli/cli.h" +#include +#include #include #include @@ -8,10 +10,11 @@ #include "absl/flags/flag.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" -#include "cli/service/command_registry.h" +#include "app/rom.h" #include "cli/handlers/agent/common.h" -#include "cli/handlers/agent/todo_commands.h" #include "cli/handlers/agent/simple_chat_command.h" +#include "cli/handlers/agent/todo_commands.h" +#include "cli/service/command_registry.h" ABSL_DECLARE_FLAG(bool, quiet); @@ -20,16 +23,26 @@ namespace cli { // Forward declarations for special agent commands (not in registry) namespace agent { +absl::Status HandleRunCommand(const std::vector& args, Rom& rom); absl::Status HandlePlanCommand(const std::vector& args); absl::Status HandleTestCommand(const std::vector& args); absl::Status HandleTestConversationCommand(const std::vector& args); absl::Status HandleLearnCommand(const std::vector& args); absl::Status HandleListCommand(); +absl::Status HandleDiffCommand(Rom& rom, const std::vector& args); +absl::Status HandleCommitCommand(Rom& rom); +absl::Status HandleRevertCommand(Rom& rom); +absl::Status HandleAcceptCommand(const std::vector& args, Rom& rom); absl::Status HandleDescribeCommand(const std::vector& args); } // namespace agent namespace { +Rom& AgentRom() { + static Rom rom; + return rom; +} + std::string GenerateAgentHelp() { auto& registry = CommandRegistry::Instance(); @@ -40,6 +53,11 @@ std::string GenerateAgentHelp() { help << " simple-chat Interactive AI chat\n"; help << " test-conversation Automated test conversation\n"; help << " plan Generate execution plan\n"; + help << " run Execute plan in sandbox\n"; + help << " diff Review the latest proposal diff\n"; + help << " accept Apply proposal changes to ROM\n"; + help << " commit Save current ROM changes\n"; + help << " revert Reload ROM from disk\n"; help << " learn Manage learned knowledge\n"; help << " todo Task management\n"; help << " test Run tests\n"; @@ -48,18 +66,20 @@ std::string GenerateAgentHelp() { // Auto-list available tool commands from registry help << "Tool Commands (AI can call these):\n"; auto agent_commands = registry.GetAgentCommands(); - int count = 0; - for (const auto& cmd : agent_commands) { - if (count++ < 10) { // Show first 10 - auto* meta = registry.GetMetadata(cmd); - if (meta) { - help << " " << cmd; - for (size_t i = cmd.length(); i < 24; i++) help << " "; - help << meta->description << "\n"; - } + const size_t preview_count = std::min(10, agent_commands.size()); + for (size_t i = 0; i < preview_count; ++i) { + const auto& cmd = agent_commands[i]; + if (auto* meta = registry.GetMetadata(cmd); meta != nullptr) { + help << " " << cmd; + for (size_t pad = cmd.length(); pad < 24; ++pad) help << " "; + help << meta->description << "\n"; } } - help << " ... and " << (agent_commands.size() - 10) << " more (see z3ed --list-commands)\n\n"; + if (agent_commands.size() > preview_count) { + help << " ... and " << (agent_commands.size() - preview_count) + << " more (see z3ed --list-commands)\n"; + } + help << "\n"; help << "Global Options:\n"; help << " --rom= Path to ROM file\n"; @@ -102,10 +122,32 @@ absl::Status HandleAgentCommand(const std::vector& arg_vec) { return registry.Execute("simple-chat", subcommand_args, nullptr); } + auto& agent_rom = AgentRom(); + + if (subcommand == "run") { + return agent::HandleRunCommand(subcommand_args, agent_rom); + } + if (subcommand == "plan") { return agent::HandlePlanCommand(subcommand_args); } + if (subcommand == "diff") { + return agent::HandleDiffCommand(agent_rom, subcommand_args); + } + + if (subcommand == "accept") { + return agent::HandleAcceptCommand(subcommand_args, agent_rom); + } + + if (subcommand == "commit") { + return agent::HandleCommitCommand(agent_rom); + } + + if (subcommand == "revert") { + return agent::HandleRevertCommand(agent_rom); + } + if (subcommand == "test") { return agent::HandleTestCommand(subcommand_args); } @@ -137,13 +179,6 @@ absl::Status HandleAgentCommand(const std::vector& arg_vec) { return agent::HandleDescribeCommand(subcommand_args); } - // Placeholder for unimplemented workflow commands - if (subcommand == "run" || subcommand == "diff" || subcommand == "accept" || - subcommand == "commit" || subcommand == "revert") { - return absl::UnimplementedError( - absl::StrCat("Agent ", subcommand, " command requires ROM context - not yet implemented")); - } - // === Registry Commands (resource, dungeon, overworld, emulator, etc.) === auto& registry = CommandRegistry::Instance(); @@ -159,7 +194,7 @@ absl::Status HandleAgentCommand(const std::vector& arg_vec) { absl::StrCat("Unknown agent command: ", subcommand)); } -// Handler functions are now implemented in command_wrappers.cc +// Handler implementations live in general_commands.cc } // namespace handlers } // namespace cli diff --git a/src/cli/handlers/agent/general_commands.cc b/src/cli/handlers/agent/general_commands.cc index 4b724fe1..13e9998e 100644 --- a/src/cli/handlers/agent/general_commands.cc +++ b/src/cli/handlers/agent/general_commands.cc @@ -29,13 +29,11 @@ #include "cli/service/ai/service_factory.h" #include "cli/service/agent/learned_knowledge_service.h" #include "cli/service/agent/proposal_executor.h" -#include "cli/service/agent/simple_chat_session.h" #include "cli/service/planning/proposal_registry.h" #include "cli/service/planning/tile16_proposal_generator.h" #include "cli/service/resources/resource_catalog.h" #include "cli/service/resources/resource_context_builder.h" #include "cli/service/rom/rom_sandbox_manager.h" -#include "cli/tui/chat_tui.h" #include "cli/cli.h" #include "util/macro.h" @@ -685,124 +683,6 @@ absl::Status HandleDescribeCommand(const std::vector& arg_vec) { return absl::OkStatus(); } -absl::Status HandleChatCommand(Rom& rom) { - RETURN_IF_ERROR(EnsureRomLoaded(rom, "agent chat")); - - // Try to load project and labels automatically - auto _ = TryLoadProjectAndLabels(rom); // Ignore errors - we'll use defaults - - tui::ChatTUI chat_tui(&rom); - chat_tui.Run(); - return absl::OkStatus(); -} - -absl::Status HandleSimpleChatCommand(const std::vector& arg_vec, - Rom* rom, bool quiet) { - RETURN_IF_ERROR(EnsureRomLoaded(*rom, "agent simple-chat")); - - auto _ = TryLoadProjectAndLabels(*rom); - - std::optional batch_file; - std::optional single_message; - bool verbose = false; - bool vim_mode = false; - std::optional format_option; - - for (size_t i = 0; i < arg_vec.size(); ++i) { - const std::string& arg = arg_vec[i]; - if (absl::StartsWith(arg, "--file=")) { - batch_file = arg.substr(7); - } else if (arg == "--file" && i + 1 < arg_vec.size()) { - batch_file = arg_vec[++i]; - } else if (absl::StartsWith(arg, "--format=")) { - format_option = arg.substr(9); - } else if (arg == "--format" && i + 1 < arg_vec.size()) { - format_option = arg_vec[++i]; - } else if (arg == "--json") { - format_option = "json"; - } else if (arg == "--markdown" || arg == "--md") { - format_option = "markdown"; - } else if (arg == "--compact" || arg == "--raw") { - format_option = "compact"; - } else if (arg == "--verbose" || arg == "-v") { - verbose = true; - } else if (arg == "--vim") { - vim_mode = true; - } else if (!absl::StartsWith(arg, "--") && !single_message.has_value()) { - single_message = arg; - } - } - - agent::AgentConfig config; - config.verbose = verbose; - config.enable_vim_mode = vim_mode; - if (format_option.has_value()) { - std::string normalized = absl::AsciiStrToLower(*format_option); - if (normalized == "json") { - config.output_format = AgentOutputFormat::kJson; - } else if (normalized == "markdown" || normalized == "md") { - config.output_format = AgentOutputFormat::kMarkdown; - } else if (normalized == "compact" || normalized == "raw") { - config.output_format = AgentOutputFormat::kCompact; - } else if (normalized == "text" || normalized == "friendly" || - normalized == "pretty") { - config.output_format = AgentOutputFormat::kFriendly; - } else { - return absl::InvalidArgumentError( - absl::StrCat("Unsupported chat format: ", *format_option, - ". Supported formats: text, markdown, json, compact")); - } - } - - SimpleChatSession session; - session.SetConfig(config); - session.SetRomContext(rom); - - if (batch_file.has_value()) { - std::ifstream file(*batch_file); - if (!file.is_open()) { - return absl::NotFoundError(absl::StrCat("Failed to open file: ", *batch_file)); - } - if (!quiet) { - std::cout << "Running batch session from: " << *batch_file << std::endl; - std::cout << "----------------------------------------\n\n"; - } - std::string line; - int line_num = 0; - while (std::getline(file, line)) { - line_num++; - std::string trimmed_line = std::string(absl::StripAsciiWhitespace(line)); - if (trimmed_line.empty() || absl::StartsWith(trimmed_line, "#")) { - continue; - } - if (!quiet) { - std::cout << "Input [" << line_num << "]: " << trimmed_line << std::endl; - } - std::string response; - auto status = session.SendAndWaitForResponse(trimmed_line, &response); - if (!status.ok()) { - std::cerr << "Error processing line " << line_num << ": " << status.message() << std::endl; - continue; - } - std::cout << response << "\n"; - if (!quiet) { - std::cout << "\n"; - } - } - return absl::OkStatus(); - } else if (single_message.has_value()) { - std::string response; - auto status = session.SendAndWaitForResponse(*single_message, &response); - if (!status.ok()) { - return status; - } - std::cout << response << "\n"; - return absl::OkStatus(); - } else { - return session.RunInteractive(); - } -} - absl::Status HandleAcceptCommand(const std::vector& arg_vec, Rom& rom) { std::optional proposal_id; diff --git a/src/cli/handlers/agent/simple_chat_command.cc b/src/cli/handlers/agent/simple_chat_command.cc index 204ebddd..c66c478d 100644 --- a/src/cli/handlers/agent/simple_chat_command.cc +++ b/src/cli/handlers/agent/simple_chat_command.cc @@ -1,6 +1,15 @@ #include "cli/handlers/agent/simple_chat_command.h" + +#include + +#include "absl/flags/declare.h" +#include "absl/flags/flag.h" +#include "absl/strings/ascii.h" +#include "absl/strings/str_cat.h" #include "cli/service/agent/simple_chat_session.h" +ABSL_DECLARE_FLAG(bool, quiet); + namespace yaze { namespace cli { namespace handlers { @@ -8,28 +17,86 @@ namespace handlers { absl::Status SimpleChatCommandHandler::Execute( Rom* rom, const resources::ArgumentParser& parser, resources::OutputFormatter& formatter) { - agent::SimpleChatSession session; session.SetRomContext(rom); - - // Configure session from parser + agent::AgentConfig config; - if (parser.HasFlag("verbose")) { - config.verbose = true; + config.verbose = parser.HasFlag("verbose"); + config.enable_vim_mode = parser.HasFlag("vim"); + + // Determine desired output format + std::optional format_arg = parser.GetString("format"); + if (parser.HasFlag("json")) format_arg = "json"; + if (parser.HasFlag("markdown") || parser.HasFlag("md")) format_arg = "markdown"; + if (parser.HasFlag("compact") || parser.HasFlag("raw")) format_arg = "compact"; + + auto select_format = [](absl::string_view value) + -> std::optional { + std::string normalized = absl::AsciiStrToLower(value); + if (normalized == "json") return agent::AgentOutputFormat::kJson; + if (normalized == "markdown" || normalized == "md") return agent::AgentOutputFormat::kMarkdown; + if (normalized == "compact" || normalized == "raw") return agent::AgentOutputFormat::kCompact; + if (normalized == "text" || normalized == "friendly" || normalized == "pretty") { + return agent::AgentOutputFormat::kFriendly; + } + return std::nullopt; + }; + + if (format_arg.has_value()) { + if (auto output_format = select_format(*format_arg); output_format.has_value()) { + config.output_format = *output_format; + } else { + return absl::InvalidArgumentError( + absl::StrCat("Unsupported chat format: ", *format_arg, + ". Supported formats: text, markdown, json, compact")); + } + } else if (absl::GetFlag(FLAGS_quiet)) { + config.output_format = agent::AgentOutputFormat::kCompact; } - if (auto format = parser.GetString("format")) { - // Simplified format handling + + std::optional prompt = parser.GetString("prompt"); + auto positional = parser.GetPositional(); + for (const auto& token : positional) { + if (token == "-v") { + config.verbose = true; + continue; + } + if (!prompt.has_value() && !token.empty() && token.front() != '-') { + prompt = token; + } } + session.SetConfig(config); - if (auto file = parser.GetString("file")) { - return session.RunBatch(*file); - } else if (auto prompt = parser.GetString("prompt")) { - std::string response; - return session.SendAndWaitForResponse(*prompt, &response); - } else { - return session.RunInteractive(); + if (auto batch = parser.GetString("file")) { + formatter.AddField("mode", "batch"); + formatter.AddField("file", *batch); + auto status = session.RunBatch(*batch); + if (status.ok()) { + formatter.AddField("status", "completed"); + } + return status; } + + if (prompt.has_value()) { + formatter.AddField("mode", "single"); + formatter.AddField("prompt", *prompt); + + std::string response; + auto status = session.SendAndWaitForResponse(*prompt, &response); + if (!status.ok()) { + return status; + } + formatter.AddField("response", response); + return absl::OkStatus(); + } + + formatter.AddField("mode", "interactive"); + auto status = session.RunInteractive(); + if (status.ok()) { + formatter.AddField("status", "completed"); + } + return status; } } // namespace handlers diff --git a/src/cli/handlers/command_handlers.cc b/src/cli/handlers/command_handlers.cc index 1675e212..ff38cfee 100644 --- a/src/cli/handlers/command_handlers.cc +++ b/src/cli/handlers/command_handlers.cc @@ -15,17 +15,11 @@ #include "cli/handlers/graphics/sprite_commands.h" #include -#include namespace yaze { namespace cli { namespace handlers { -// Static command registry -namespace { -std::unordered_map g_command_registry; -} - std::vector> CreateCliCommandHandlers() { std::vector> handlers; @@ -133,15 +127,6 @@ std::vector> CreateAllCommandHandlers return handlers; } -resources::CommandHandler* GetCommandHandler(const std::string& name) { - auto it = g_command_registry.find(name); - if (it != g_command_registry.end()) { - return it->second; - } - return nullptr; -} - } // namespace handlers } // namespace cli } // namespace yaze - diff --git a/src/cli/handlers/command_handlers.h b/src/cli/handlers/command_handlers.h index 7b7085b0..238cf98c 100644 --- a/src/cli/handlers/command_handlers.h +++ b/src/cli/handlers/command_handlers.h @@ -94,14 +94,6 @@ std::vector> CreateAgentCommandHandle */ std::vector> CreateAllCommandHandlers(); -/** - * @brief Get a command handler by name - * - * @param name Command name (e.g., "resource-list", "hex-read") - * @return Pointer to command handler or nullptr if not found - */ -resources::CommandHandler* GetCommandHandler(const std::string& name); - } // namespace handlers } // namespace cli } // namespace yaze