Update z3ed CLI tool and project build configuration

- Updated `.clang-tidy` and `.clangd` configurations for improved code quality checks and diagnostics.
- Added new submodules for JSON and HTTP libraries to support future features.
- Refined README and documentation files to standardize naming conventions and improve clarity.
- Introduced a new command palette in the CLI for easier command access and execution.
- Implemented various CLI handlers for managing ROM, sprites, palettes, and dungeon functionalities.
- Enhanced the TUI components for better user interaction and command execution.
- Added AI service integration for generating commands based on user prompts, expanding the CLI's capabilities.
This commit is contained in:
scawful
2025-10-01 08:57:10 -04:00
parent e7d4f5ea02
commit ba50d89e7d
46 changed files with 2421 additions and 965 deletions

232
src/cli/handlers/agent.cc Normal file
View File

@@ -0,0 +1,232 @@
#include "cli/z3ed.h"
#include "cli/modern_cli.h"
#include "cli/service/ai_service.h"
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
namespace yaze {
namespace cli {
// Mock AI service is defined in ai_service.h
namespace {
absl::Status HandleRunCommand(const std::vector<std::string>& arg_vec, Rom& rom) {
if (arg_vec.size() < 2 || arg_vec[0] != "--prompt") {
return absl::InvalidArgumentError("Usage: agent run --prompt <prompt>");
}
std::string prompt = arg_vec[1];
// Save a temporary copy of the ROM
if (rom.is_loaded()) {
auto status = rom.SaveToFile({.save_new = true, .filename = "temp_rom.sfc"});
if (!status.ok()) {
return status;
}
}
MockAIService ai_service;
auto commands_or = ai_service.GetCommands(prompt);
if (!commands_or.ok()) {
return commands_or.status();
}
std::vector<std::string> commands = commands_or.value();
ModernCLI cli;
for (const auto& command : commands) {
std::vector<std::string> command_parts;
std::string current_part;
bool in_quotes = false;
for (char c : command) {
if (c == '\"') {
in_quotes = !in_quotes;
} else if (c == ' ' && !in_quotes) {
command_parts.push_back(current_part);
current_part.clear();
} else {
current_part += c;
}
}
command_parts.push_back(current_part);
std::string cmd_name = command_parts[0] + " " + command_parts[1];
std::vector<std::string> cmd_args(command_parts.begin() + 2, command_parts.end());
auto it = cli.commands_.find(cmd_name);
if (it != cli.commands_.end()) {
auto status = it->second.handler(cmd_args);
if (!status.ok()) {
return status;
}
}
}
return absl::OkStatus();
}
absl::Status HandlePlanCommand(const std::vector<std::string>& arg_vec) {
if (arg_vec.size() < 2 || arg_vec[0] != "--prompt") {
return absl::InvalidArgumentError("Usage: agent plan --prompt <prompt>");
}
std::string prompt = arg_vec[1];
MockAIService ai_service;
auto commands_or = ai_service.GetCommands(prompt);
if (!commands_or.ok()) {
return commands_or.status();
}
std::vector<std::string> commands = commands_or.value();
std::cout << "AI Agent Plan:" << std::endl;
for (const auto& command : commands) {
std::cout << " - " << command << std::endl;
}
return absl::OkStatus();
}
absl::Status HandleDiffCommand(Rom& rom) {
if (rom.is_loaded()) {
RomDiff diff_handler;
auto status = diff_handler.Run({rom.filename(), "temp_rom.sfc"});
if (!status.ok()) {
return status;
}
} else {
return absl::AbortedError("No ROM loaded.");
}
return absl::OkStatus();
}
absl::Status HandleTestCommand(const std::vector<std::string>& arg_vec) {
if (arg_vec.size() < 2 || arg_vec[0] != "--test") {
return absl::InvalidArgumentError("Usage: agent test --test <test_name>");
}
std::string test_name = arg_vec[1];
#ifdef _WIN32
return absl::UnimplementedError(
"GUI test command is not supported on Windows. "
"Please run yaze_test.exe directly with --enable-ui-tests flag.");
#else
char exe_path[1024];
#ifdef __APPLE__
uint32_t size = sizeof(exe_path);
if (_NSGetExecutablePath(exe_path, &size) != 0) {
return absl::InternalError("Could not get executable path");
}
#else
ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
if (len == -1) {
return absl::InternalError("Could not get executable path");
}
exe_path[len] = '\0';
#endif
std::string exe_dir = std::string(exe_path);
exe_dir = exe_dir.substr(0, exe_dir.find_last_of("/"));
std::string yaze_test_path = exe_dir + "/yaze_test";
std::vector<std::string> command_args;
command_args.push_back(yaze_test_path);
command_args.push_back("--enable-ui-tests");
command_args.push_back("--test=" + test_name);
std::vector<char*> argv;
for (const auto& arg : command_args) {
argv.push_back((char*)arg.c_str());
}
argv.push_back(nullptr);
pid_t pid = fork();
if (pid == -1) {
return absl::InternalError("Failed to fork process");
}
if (pid == 0) {
// Child process
execv(yaze_test_path.c_str(), argv.data());
// If execv returns, it must have failed
return absl::InternalError("Failed to execute yaze_test");
} else {
// Parent process
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
if (exit_code == 0) {
return absl::OkStatus();
} else {
return absl::InternalError(absl::StrFormat("yaze_test exited with code %d", exit_code));
}
}
}
return absl::OkStatus();
#endif
}
absl::Status HandleLearnCommand() {
std::cout << "Agent learn not yet implemented." << std::endl;
return absl::OkStatus();
}
absl::Status HandleCommitCommand(Rom& rom) {
if (rom.is_loaded()) {
auto status = rom.SaveToFile({.save_new = false});
if (!status.ok()) {
return status;
}
std::cout << "✅ Changes committed successfully." << std::endl;
} else {
return absl::AbortedError("No ROM loaded.");
}
return absl::OkStatus();
}
absl::Status HandleRevertCommand(Rom& rom) {
if (rom.is_loaded()) {
auto status = rom.LoadFromFile(rom.filename());
if (!status.ok()) {
return status;
}
std::cout << "✅ Changes reverted successfully." << std::endl;
} else {
return absl::AbortedError("No ROM loaded.");
}
return absl::OkStatus();
}
} // namespace
absl::Status Agent::Run(const std::vector<std::string>& arg_vec) {
if (arg_vec.empty()) {
return absl::InvalidArgumentError("Usage: agent <run|plan|diff|test|learn|commit|revert> [options]");
}
std::string subcommand = arg_vec[0];
std::vector<std::string> subcommand_args(arg_vec.begin() + 1, arg_vec.end());
if (subcommand == "run") {
return HandleRunCommand(subcommand_args, rom_);
} else if (subcommand == "plan") {
return HandlePlanCommand(subcommand_args);
} else if (subcommand == "diff") {
return HandleDiffCommand(rom_);
} else if (subcommand == "test") {
return HandleTestCommand(subcommand_args);
} else if (subcommand == "learn") {
return HandleLearnCommand();
} else if (subcommand == "commit") {
return HandleCommitCommand(rom_);
} else if (subcommand == "revert") {
return HandleRevertCommand(rom_);
} else {
return absl::InvalidArgumentError("Invalid subcommand for agent command.");
}
return absl::OkStatus();
}
} // namespace cli
} // namespace yaze