feat(build-system): enhance CMake configuration and introduce new utility files

- Refactored CMakeLists.txt to streamline project configuration and improve readability.
- Introduced new utility functions in `utils.cmake` for setting compiler flags and managing dependencies.
- Added `dependencies.cmake` to centralize third-party dependency management, enhancing modularity.
- Updated CI workflows to include new build options and improved logging for better feedback during configuration.
- Implemented precompiled headers in various libraries to speed up compilation times.

Benefits:
- Improved maintainability and clarity of the build system.
- Enhanced build performance through precompiled headers.
- Streamlined dependency management for easier integration of third-party libraries.
This commit is contained in:
scawful
2025-10-11 02:44:17 -04:00
parent 31d0337b11
commit f54949bdd8
56 changed files with 2673 additions and 4872 deletions

View File

@@ -1,69 +1,3 @@
include(FetchContent)
function(_yaze_ensure_yaml_cpp _out_target)
if(TARGET yaml-cpp::yaml-cpp)
set(${_out_target} yaml-cpp::yaml-cpp PARENT_SCOPE)
return()
endif()
if(TARGET yaml-cpp)
set(${_out_target} yaml-cpp PARENT_SCOPE)
return()
endif()
find_package(yaml-cpp CONFIG QUIET)
if(TARGET yaml-cpp::yaml-cpp)
set(${_out_target} yaml-cpp::yaml-cpp PARENT_SCOPE)
return()
elseif(TARGET yaml-cpp)
set(${_out_target} yaml-cpp PARENT_SCOPE)
return()
endif()
message(STATUS "yaml-cpp not found via package config, fetching from source")
FetchContent_Declare(yaml-cpp
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
GIT_TAG 0.8.0
)
FetchContent_GetProperties(yaml-cpp)
if(NOT yaml-cpp_POPULATED)
FetchContent_Populate(yaml-cpp)
set(_yaml_cpp_cmakelists "${yaml-cpp_SOURCE_DIR}/CMakeLists.txt")
if(EXISTS "${_yaml_cpp_cmakelists}")
file(READ "${_yaml_cpp_cmakelists}" _yaml_cpp_cmake_contents)
if(_yaml_cpp_cmake_contents MATCHES "cmake_minimum_required\\(VERSION 3\\.4\\)")
string(REPLACE "cmake_minimum_required(VERSION 3.4)"
"cmake_minimum_required(VERSION 3.5)"
_yaml_cpp_cmake_contents "${_yaml_cpp_cmake_contents}")
file(WRITE "${_yaml_cpp_cmakelists}" "${_yaml_cpp_cmake_contents}")
endif()
endif()
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Disable yaml-cpp tests" FORCE)
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Disable yaml-cpp contrib" FORCE)
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Disable yaml-cpp tools" FORCE)
set(YAML_CPP_INSTALL OFF CACHE BOOL "Disable yaml-cpp install" FORCE)
set(YAML_CPP_FORMAT_SOURCE OFF CACHE BOOL "Disable yaml-cpp format target" FORCE)
add_subdirectory(${yaml-cpp_SOURCE_DIR} ${yaml-cpp_BINARY_DIR} EXCLUDE_FROM_ALL)
if(NOT TARGET yaml-cpp)
message(FATAL_ERROR "yaml-cpp target was not created after fetching")
endif()
# Ensure the fetched target exposes its public headers
target_include_directories(yaml-cpp PUBLIC ${yaml-cpp_SOURCE_DIR}/include)
endif()
set(${_out_target} yaml-cpp PARENT_SCOPE)
endfunction()
_yaze_ensure_yaml_cpp(YAZE_YAML_CPP_TARGET)
set(YAZE_AGENT_SOURCES
cli/service/agent/proposal_executor.cc
cli/handlers/agent/todo_commands.cc
@@ -73,6 +7,7 @@ set(YAZE_AGENT_SOURCES
cli/service/agent/tool_dispatcher.cc
cli/service/agent/learned_knowledge_service.cc
cli/service/agent/todo_manager.cc
cli/service/agent/vim_mode.cc
cli/service/ai/ai_service.cc
cli/service/ai/ai_action_parser.cc
cli/service/ai/vision_action_refiner.cc
@@ -91,8 +26,9 @@ set(YAZE_AGENT_SOURCES
cli/service/resources/resource_context_builder.cc
cli/service/resources/command_context.cc
cli/service/resources/command_handler.cc
cli/handlers/command_wrappers.cc
cli/handlers/agent.cc
cli/handlers/command_handlers.cc
cli/handlers/agent/simple_chat_command.cc
cli/handlers/game/overworld_inspect.cc
cli/handlers/game/message.cc
cli/handlers/rom/mock_rom.cc
@@ -107,6 +43,7 @@ set(YAZE_AGENT_SOURCES
cli/handlers/graphics/palette_commands.cc
cli/handlers/tools/emulator_commands.cc
cli/handlers/game/message_commands.cc
cli/handlers/graphics/sprite_commands.cc
# ROM commands
cli/handlers/rom/rom_commands.cc
cli/handlers/rom/project_commands.cc
@@ -122,8 +59,14 @@ add_library(yaze_agent STATIC ${YAZE_AGENT_SOURCES})
set(_yaze_agent_link_targets
yaze_common
yaze_util
yaze_gfx
yaze_gui
yaze_core_lib
yaze_zelda3
yaze_emulator
${ABSL_TARGETS}
${YAZE_YAML_CPP_TARGET}
yaml-cpp
)
target_link_libraries(yaze_agent PUBLIC ${_yaze_agent_link_targets})
@@ -135,15 +78,9 @@ target_include_directories(yaze_agent
${CMAKE_SOURCE_DIR}/third_party/httplib
${CMAKE_SOURCE_DIR}/third_party/json/include
${CMAKE_SOURCE_DIR}/src/lib
${CMAKE_SOURCE_DIR}/src/cli/handlers
)
if(YAZE_YAML_CPP_TARGET)
get_target_property(_yaze_yaml_include_dirs ${YAZE_YAML_CPP_TARGET} INTERFACE_INCLUDE_DIRECTORIES)
if(_yaze_yaml_include_dirs)
target_include_directories(yaze_agent PUBLIC ${_yaze_yaml_include_dirs})
endif()
endif()
if(SDL2_INCLUDE_DIR)
target_include_directories(yaze_agent PUBLIC ${SDL2_INCLUDE_DIR})
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,10 @@
#include "app/snes.h"
#include "util/macro.h"
#include "cli/service/resources/command_handler.h"
#include <map>
#include <memory>
// Forward declarations
namespace ftxui {
class ScreenInteractive;
@@ -24,215 +28,21 @@ namespace cli {
// Forward declaration
class TuiComponent;
class CommandHandler {
public:
CommandHandler() = default;
virtual ~CommandHandler() = default;
virtual absl::Status Run(const std::vector<std::string>& arg_vec) = 0;
virtual void RunTUI(ftxui::ScreenInteractive& screen) {
// Default implementation does nothing
}
Rom rom_;
};
struct CommandInfo {
std::string name;
std::string description;
std::string usage;
std::function<absl::Status(const std::vector<std::string>&)> handler;
};
class ModernCLI {
public:
ModernCLI();
absl::Status Run(int argc, char* argv[]);
CommandHandler* GetCommandHandler(const std::string& name);
void PrintTopLevelHelp() const;
void PrintCategoryHelp(const std::string& category) const;
void PrintCommandSummary() const;
std::map<std::string, CommandInfo> commands_;
private:
void SetupCommands();
void ShowHelp();
void ShowCategoryHelp(const std::string& category);
void ShowCategoryHelp(const std::string& category) const;
void ShowCommandSummary() const;
// Command Handlers
absl::Status HandleAsarPatchCommand(const std::vector<std::string>& args);
absl::Status HandleBpsPatchCommand(const std::vector<std::string>& args);
absl::Status HandleExtractSymbolsCommand(const std::vector<std::string>& args);
absl::Status HandleAgentCommand(const std::vector<std::string>& args);
absl::Status HandleCollabCommand(const std::vector<std::string>& args);
absl::Status HandleProjectBuildCommand(const std::vector<std::string>& args);
absl::Status HandleProjectInitCommand(const std::vector<std::string>& args);
absl::Status HandleRomInfoCommand(const std::vector<std::string>& args);
absl::Status HandleRomGenerateGoldenCommand(const std::vector<std::string>& args);
absl::Status HandleRomDiffCommand(const std::vector<std::string>& args);
absl::Status HandleDungeonExportCommand(const std::vector<std::string>& args);
absl::Status HandleDungeonListObjectsCommand(const std::vector<std::string>& args);
absl::Status HandleGfxExportCommand(const std::vector<std::string>& args);
absl::Status HandleGfxImportCommand(const std::vector<std::string>& args);
absl::Status HandleCommandPaletteCommand(const std::vector<std::string>& args);
absl::Status HandlePaletteExportCommand(const std::vector<std::string>& args);
absl::Status HandlePaletteImportCommand(const std::vector<std::string>& args);
absl::Status HandlePaletteCommand(const std::vector<std::string>& args);
absl::Status HandleRomValidateCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldGetTileCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldFindTileCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldDescribeMapCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldListWarpsCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldSetTileCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldSelectRectCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldScrollToCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldSetZoomCommand(const std::vector<std::string>& args);
absl::Status HandleOverworldGetVisibleRegionCommand(const std::vector<std::string>& args);
absl::Status HandleSpriteCreateCommand(const std::vector<std::string>& args);
absl::Status HandleChatEntryCommand(const std::vector<std::string>& args);
absl::Status HandleProposalCommand(const std::vector<std::string>& args);
absl::Status HandleWidgetCommand(const std::vector<std::string>& args);
};
// Legacy command classes removed - using new CommandHandler system
// See TODO comments in cli.cc for implementation roadmap
class Open : public CommandHandler {
public:
absl::Status Run(const std::vector<std::string>& arg_vec) override {
auto const& arg = arg_vec[0];
RETURN_IF_ERROR(rom_.LoadFromFile(arg, RomLoadOptions::CliDefaults()))
std::cout << "Title: " << rom_.title() << std::endl;
std::cout << "Size: 0x" << std::hex << rom_.size() << std::endl;
return absl::OkStatus();
}
};
class Backup : public CommandHandler {
public:
absl::Status Run(const std::vector<std::string>& arg_vec) override {
RETURN_IF_ERROR(rom_.LoadFromFile(arg_vec[0]))
Rom::SaveSettings settings;
settings.backup = true;
if (arg_vec.size() == 2) {
// Optional filename added
settings.filename = arg_vec[1];
}
RETURN_IF_ERROR(rom_.SaveToFile(settings))
return absl::OkStatus();
}
};
class Compress : public CommandHandler {
public:
absl::Status Run(const std::vector<std::string>& arg_vec) override;
};
class Decompress : public CommandHandler {
public:
absl::Status Run(const std::vector<std::string>& arg_vec) override;
};
/**
* @brief Convert a SNES address to a PC address.
* @param arg_vec `-s <address>`
* @return absl::Status
*/
class SnesToPcCommand : public CommandHandler {
public:
absl::Status Run(const std::vector<std::string>& arg_vec) override {
auto arg = arg_vec[0];
std::stringstream ss(arg.data());
uint32_t snes_address;
ss >> std::hex >> snes_address;
uint32_t pc_address = SnesToPc(snes_address);
std::cout << std::hex << pc_address << std::endl;
return absl::OkStatus();
}
};
/**
* @brief Convert a PC address to a SNES address.
* @param arg_vec `-p <address>`
* @return absl::Status
*/
class PcToSnesCommand : public CommandHandler {
public:
absl::Status Run(const std::vector<std::string>& arg_vec) override {
auto arg = arg_vec[0];
std::stringstream ss(arg.data());
uint32_t pc_address;
ss >> std::hex >> pc_address;
uint32_t snes_address = PcToSnes(pc_address);
std::cout << "SNES LoROM Address: ";
std::cout << "$" << std::uppercase << std::hex << snes_address << "\n";
return absl::OkStatus();
}
};
/**
* @brief Read from a Rom file.
* @param arg_vec `-r <rom_file> <address> <optional:length, default: 0x01>`
* @return absl::Status
*/
class ReadFromRom : public CommandHandler {
public:
absl::Status Run(const std::vector<std::string>& arg_vec) override {
RETURN_IF_ERROR(rom_.LoadFromFile(arg_vec[0]))
std::stringstream ss(arg_vec[1].data());
uint32_t offset;
ss >> std::hex >> offset;
uint32_t length = 0x01;
if (!arg_vec[2].empty()) {
length = std::stoi(arg_vec[2]);
}
if (length > 1) {
auto returned_bytes_status = rom_.ReadByteVector(offset, length);
if (!returned_bytes_status.ok()) {
return returned_bytes_status.status();
}
auto returned_bytes = returned_bytes_status.value();
for (const auto& each : returned_bytes) {
std::cout << each;
}
std::cout << std::endl;
} else {
auto byte = rom_.ReadByte(offset);
std::cout << std::hex << byte.value() << std::endl;
}
return absl::OkStatus();
}
};
/**
* @brief Expand a Rom file.
* @param arg_vec `-x <rom_file> <file_size>`
* @return absl::Status
*/
class Expand : public CommandHandler {
public:
absl::Status Run(const std::vector<std::string>& arg_vec) override {
RETURN_IF_ERROR(rom_.LoadFromFile(arg_vec[0]))
std::stringstream ss(arg_vec[1].data());
uint32_t size;
ss >> std::hex >> size;
rom_.Expand(size);
std::cout << "Successfully expanded ROM to " << std::hex << size
<< std::endl;
return absl::OkStatus();
}
std::map<std::string, std::unique_ptr<resources::CommandHandler>> commands_;
};
} // namespace cli

320
src/cli/handlers/README.md Normal file
View File

@@ -0,0 +1,320 @@
# YAZE Modern Command Handler Architecture
This directory contains the modern command handler system that provides a consistent interface for both CLI and AI agent interactions with ROM data.
## Architecture Overview
The command handler system follows a clean, layered architecture:
```
┌─────────────────────────────────────────┐
│ CLI / Agent Interface │
│ (cli.cc, agent.cc, simple-chat, etc) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Command Handler Base Class │
│ (resources/command_handler.h) │
│ - Argument parsing │
│ - ROM context management │
│ - Output formatting │
│ - Error handling │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Concrete Command Handlers │
│ (handlers/*/*) │
│ - SpriteListCommandHandler │
│ - DungeonDescribeRoomCommandHandler │
│ - OverworldFindTileCommandHandler │
│ - PaletteGetColorsCommandHandler │
│ - etc... │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Core YAZE Libraries │
│ - zelda3/ (overworld, dungeon, sprite) │
│ - gfx/ (graphics, palette) │
│ - app/editor/ (ROM operations) │
└─────────────────────────────────────────┘
```
## Namespace Structure
All command handlers are organized under a simplified namespace:
```cpp
namespace yaze {
namespace cli {
namespace handlers {
// All command handler classes live here
class SpriteListCommandHandler : public resources::CommandHandler { ... };
class DungeonDescribeRoomCommandHandler : public resources::CommandHandler { ... };
// etc.
}
}
}
```
## Directory Organization
```
handlers/
├── README.md (this file)
├── commands.h // Legacy command function declarations
├── command_wrappers.cc // Wrapper functions for backward compatibility
├── command_handlers.h // Forward declarations and factory functions
├── command_handlers.cc // Factory implementations
├── graphics/ // Graphics-related commands
│ ├── sprite_commands.h/.cc // Sprite listing and properties
│ ├── palette_commands.h/.cc // Palette manipulation
│ ├── hex_commands.h/.cc // Raw hex data access
│ └── gfx.cc // Legacy graphics commands
├── game/ // Game data inspection
│ ├── dungeon_commands.h/.cc // Dungeon room inspection
│ ├── overworld_commands.h/.cc // Overworld map inspection
│ ├── music_commands.h/.cc // Music track information
│ ├── dialogue_commands.h/.cc // Dialogue/message search
│ └── message_commands.h/.cc // Message data access
├── tools/ // Development tools
│ ├── resource_commands.h/.cc // Resource label inspection
│ ├── gui_commands.h/.cc // GUI automation
│ └── emulator_commands.h/.cc // Emulator/debugger control
└── agent/ // AI agent specific
├── general_commands.cc // Agent command routing
├── test_commands.cc // Test harness
└── todo_commands.h/.cc // Task management
```
## Creating a New Command Handler
### 1. Define the Handler Class
Create a header file (e.g., `new_feature_commands.h`):
```cpp
#ifndef YAZE_SRC_CLI_HANDLERS_NEW_FEATURE_COMMANDS_H_
#define YAZE_SRC_CLI_HANDLERS_NEW_FEATURE_COMMANDS_H_
#include "cli/service/resources/command_handler.h"
namespace yaze {
namespace cli {
namespace handlers {
class NewFeatureCommandHandler : public resources::CommandHandler {
public:
std::string GetName() const { return "new-feature"; }
std::string GetDescription() const {
return "Brief description of what this command does";
}
std::string GetUsage() const {
return "new-feature --arg1 <value> [--optional-arg2 <value>]";
}
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
// Validate required arguments
return parser.RequireArgs({"arg1"});
}
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
resources::OutputFormatter& formatter) override;
};
} // namespace handlers
} // namespace cli
} // namespace yaze
#endif // YAZE_SRC_CLI_HANDLERS_NEW_FEATURE_COMMANDS_H_
```
### 2. Implement the Handler
Create the implementation file (e.g., `new_feature_commands.cc`):
```cpp
#include "cli/handlers/new_feature_commands.h"
#include "absl/strings/str_format.h"
namespace yaze {
namespace cli {
namespace handlers {
absl::Status NewFeatureCommandHandler::Execute(
Rom* rom, const resources::ArgumentParser& parser,
resources::OutputFormatter& formatter) {
// Parse arguments
auto arg1 = parser.GetString("arg1").value();
auto arg2 = parser.GetString("optional-arg2").value_or("default");
// Begin output
formatter.BeginObject("New Feature Result");
formatter.AddField("input_arg", arg1);
// Do work with ROM
// ... use rom->read(), zelda3 classes, etc.
// Add results to formatter
formatter.AddField("result", "success");
formatter.BeginArray("items");
// ... add items
formatter.EndArray();
formatter.EndObject();
return absl::OkStatus();
}
} // namespace handlers
} // namespace cli
} // namespace yaze
```
### 3. Register in Factory
Add to `command_handlers.cc`:
```cpp
#include "cli/handlers/new_feature_commands.h"
std::vector<std::unique_ptr<resources::CommandHandler>> CreateCliCommandHandlers() {
// ... existing handlers ...
handlers.push_back(std::make_unique<NewFeatureCommandHandler>());
return handlers;
}
```
### 4. Add Forward Declaration
Add to `command_handlers.h`:
```cpp
// Forward declarations for command handler classes
class NewFeatureCommandHandler;
```
## Command Handler Base Class
The `resources::CommandHandler` base class provides:
### Lifecycle Methods
- `Run(args, rom_context)` - Main entry point that orchestrates the full command execution
- `ValidateArgs(parser)` - Override to validate command arguments
- `Execute(rom, parser, formatter)` - Override to implement command logic
### Helper Methods
- `GetUsage()` - Return usage string for help
- `GetName()` - Return command name
- `GetDescription()` - Return brief description
- `RequiresLabels()` - Return true if command needs ROM labels loaded
- `GetDefaultFormat()` - Return "json" or "text" for default output
- `GetOutputTitle()` - Return title for output object
## Argument Parsing
The `ArgumentParser` class handles common CLI patterns:
```cpp
// Get string argument
auto value = parser.GetString("arg_name").value_or("default");
// Get integer (supports hex with 0x prefix)
auto count = parser.GetInt("count").value_or(10);
// Get hex value
auto address = parser.GetHex("address").value();
// Check for flag
if (parser.HasFlag("verbose")) { ... }
// Get positional arguments
auto positional = parser.GetPositional();
// Require specific arguments
RETURN_IF_ERROR(parser.RequireArgs({"required1", "required2"}));
```
## Output Formatting
The `OutputFormatter` class provides consistent JSON/text output:
```cpp
// Begin object
formatter.BeginObject("Title");
// Add fields
formatter.AddField("string_field", "value");
formatter.AddField("int_field", 42);
formatter.AddField("bool_field", true);
formatter.AddHexField("address", 0x1234, 4); // Width in digits
// Arrays
formatter.BeginArray("items");
for (const auto& item : items) {
formatter.AddArrayItem(absl::StrFormat("Item %d", i));
}
formatter.EndArray();
// Nested objects
formatter.BeginObject("nested");
formatter.AddField("nested_field", "value");
formatter.EndObject();
// End object
formatter.EndObject();
```
## Integration with Public API
Command handlers are designed to work alongside the public C API defined in `incl/yaze.h` and `incl/zelda.h`.
- Handlers use internal C++ classes from `app/zelda3/`
- Output structures align with C API data types where possible
- Future: C API bridge will expose commands to external applications
## Best Practices
1. **Keep handlers focused** - One command per handler class
2. **Use existing zelda3 classes** - Don't duplicate ROM parsing logic
3. **Validate inputs early** - Use `ValidateArgs()` to catch errors
4. **Provide good error messages** - Return descriptive `absl::Status` errors
5. **Support both JSON and text** - Format output using `OutputFormatter`
6. **Document parameters** - Include full usage string in `GetUsage()`
7. **Test with agents** - Commands should be AI-friendly
8. **Mark unused rom parameter** - Use `Rom* /*rom*/` if not needed
## Testing
Test commands using the CLI:
```bash
# Direct command execution
./build/bin/z3ed agent sprite-list --limit 10 --format json --rom zelda3.sfc
# Via simple-chat interface
./build/bin/z3ed agent simple-chat --rom zelda3.sfc
> sprite-list --limit 5
# In agent test suite
./build/bin/z3ed agent test-conversation --rom zelda3.sfc
```
## Future Enhancements
- [ ] C API bridge for external language bindings
- [ ] Command auto-discovery and registration
- [ ] Per-command help system
- [ ] Command aliases and shortcuts
- [ ] Batch command execution
- [ ] Command pipelines (output of one → input of another)
- [ ] Interactive command REPL improvements

View File

@@ -1,4 +1,3 @@
#include "cli/handlers/commands.h"
#include "cli/handlers/agent/todo_commands.h"
#include "cli/cli.h"
@@ -8,19 +7,70 @@
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/status/status.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/tools/resource_commands.h"
#include "cli/handlers/game/dungeon_commands.h"
#include "cli/handlers/game/overworld_commands.h"
#include "cli/handlers/tools/gui_commands.h"
#include "cli/handlers/tools/emulator_commands.h"
ABSL_DECLARE_FLAG(bool, quiet);
namespace yaze {
namespace cli {
// Forward declarations from general_commands.cc
namespace agent {
absl::Status HandlePlanCommand(const std::vector<std::string>& args);
absl::Status HandleTestCommand(const std::vector<std::string>& args);
absl::Status HandleTestConversationCommand(const std::vector<std::string>& args);
absl::Status HandleGuiCommand(const std::vector<std::string>& args);
absl::Status HandleLearnCommand(const std::vector<std::string>& args);
absl::Status HandleListCommand();
absl::Status HandleDescribeCommand(const std::vector<std::string>& args);
// Wrapper functions to call CommandHandlers
absl::Status HandleResourceListCommand(const std::vector<std::string>& args, Rom* rom) {
handlers::ResourceListCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleResourceSearchCommand(const std::vector<std::string>& args, Rom* rom) {
handlers::ResourceSearchCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDungeonListSpritesCommand(const std::vector<std::string>& args, Rom* rom) {
handlers::DungeonListSpritesCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDungeonDescribeRoomCommand(const std::vector<std::string>& args, Rom* rom) {
handlers::DungeonDescribeRoomCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleOverworldFindTileCommand(const std::vector<std::string>& args, Rom* rom) {
handlers::OverworldFindTileCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleOverworldDescribeMapCommand(const std::vector<std::string>& args, Rom* rom) {
handlers::OverworldDescribeMapCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleOverworldListWarpsCommand(const std::vector<std::string>& args, Rom* rom) {
handlers::OverworldListWarpsCommandHandler handler;
return handler.Run(args, rom);
}
} // namespace agent
namespace {
// Forward declarations for functions implemented in other files
// Function declarations moved to commands.h
// Use handlers from command_wrappers.cc
using namespace yaze::cli::handlers;
constexpr absl::string_view kUsage =
"Usage: agent <subcommand> [options]\n"
"\n"
@@ -98,13 +148,11 @@ absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec) {
const std::string& subcommand = arg_vec[0];
std::vector<std::string> subcommand_args(arg_vec.begin() + 1, arg_vec.end());
// TODO: Implement proper ROM context handling
// For now, return unimplemented for commands that require ROM context
if (subcommand == "run") {
return absl::UnimplementedError("Agent run command requires ROM context - not yet implemented");
}
if (subcommand == "plan") {
return HandlePlanCommand(subcommand_args);
return agent::HandlePlanCommand(subcommand_args);
}
if (subcommand == "diff") {
return absl::UnimplementedError("Agent diff command requires ROM context - not yet implemented");
@@ -113,19 +161,19 @@ absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec) {
return absl::UnimplementedError("Agent accept command requires ROM context - not yet implemented");
}
if (subcommand == "test") {
return HandleTestCommand(subcommand_args);
return agent::HandleTestCommand(subcommand_args);
}
if (subcommand == "test-conversation") {
return HandleTestConversationCommand(subcommand_args);
return agent::HandleTestConversationCommand(subcommand_args);
}
if (subcommand == "gui") {
return HandleGuiCommand(subcommand_args);
return agent::HandleGuiCommand(subcommand_args);
}
if (subcommand == "learn") {
return HandleLearnCommand(subcommand_args);
return agent::HandleLearnCommand(subcommand_args);
}
if (subcommand == "list") {
return HandleListCommand();
return agent::HandleListCommand();
}
if (subcommand == "commit") {
return absl::UnimplementedError("Agent commit command requires ROM context - not yet implemented");
@@ -134,60 +182,57 @@ absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec) {
return absl::UnimplementedError("Agent revert command requires ROM context - not yet implemented");
}
if (subcommand == "describe") {
return HandleDescribeCommand(subcommand_args);
return agent::HandleDescribeCommand(subcommand_args);
}
if (subcommand == "resource-list") {
return HandleResourceListCommand(subcommand_args, nullptr);
return agent::HandleResourceListCommand(subcommand_args, nullptr);
}
if (subcommand == "resource-search") {
return HandleResourceSearchCommand(subcommand_args, nullptr);
return agent::HandleResourceSearchCommand(subcommand_args, nullptr);
}
if (subcommand == "dungeon-list-sprites") {
return HandleDungeonListSpritesCommand(subcommand_args, nullptr);
return agent::HandleDungeonListSpritesCommand(subcommand_args, nullptr);
}
if (subcommand == "dungeon-describe-room") {
return HandleDungeonDescribeRoomCommand(subcommand_args, nullptr);
return agent::HandleDungeonDescribeRoomCommand(subcommand_args, nullptr);
}
if (subcommand == "overworld-find-tile") {
return HandleOverworldFindTileCommand(subcommand_args, nullptr);
return agent::HandleOverworldFindTileCommand(subcommand_args, nullptr);
}
if (subcommand == "overworld-describe-map") {
return HandleOverworldDescribeMapCommand(subcommand_args, nullptr);
return agent::HandleOverworldDescribeMapCommand(subcommand_args, nullptr);
}
if (subcommand == "overworld-list-warps") {
return HandleOverworldListWarpsCommand(subcommand_args, nullptr);
}
if (subcommand == "chat") {
return absl::UnimplementedError("Agent chat command requires ROM context - not yet implemented");
}
if (subcommand == "simple-chat") {
return absl::UnimplementedError("Agent simple-chat command requires ROM context - not yet implemented");
}
if (subcommand == "todo") {
return handlers::HandleTodoCommand(subcommand_args);
return agent::HandleOverworldListWarpsCommand(subcommand_args, nullptr);
}
// if (subcommand == "chat") {
// return absl::UnimplementedError("Agent chat command requires ROM context - not yet implemented");
// }
// if (subcommand == "todo") {
// return handlers::HandleTodoCommand(subcommand_args);
// }
// Hex manipulation commands
if (subcommand == "hex-read") {
return HandleHexRead(subcommand_args, nullptr);
}
if (subcommand == "hex-write") {
return HandleHexWrite(subcommand_args, nullptr);
}
if (subcommand == "hex-search") {
return HandleHexSearch(subcommand_args, nullptr);
}
// // Hex manipulation commands
// if (subcommand == "hex-read") {
// return HandleHexRead(subcommand_args, nullptr);
// }
// if (subcommand == "hex-write") {
// return HandleHexWrite(subcommand_args, nullptr);
// }
// if (subcommand == "hex-search") {
// return HandleHexSearch(subcommand_args, nullptr);
// }
// Palette manipulation commands
if (subcommand == "palette-get-colors") {
return HandlePaletteGetColors(subcommand_args, nullptr);
}
if (subcommand == "palette-set-color") {
return HandlePaletteSetColor(subcommand_args, nullptr);
}
if (subcommand == "palette-analyze") {
return HandlePaletteAnalyze(subcommand_args, nullptr);
}
// // Palette manipulation commands
// if (subcommand == "palette-get-colors") {
// return HandlePaletteGetColors(subcommand_args, nullptr);
// }
// if (subcommand == "palette-set-color") {
// return HandlePaletteSetColor(subcommand_args, nullptr);
// }
// if (subcommand == "palette-analyze") {
// return HandlePaletteAnalyze(subcommand_args, nullptr);
// }
return absl::InvalidArgumentError(std::string(kUsage));
}

View File

@@ -0,0 +1,37 @@
#include "cli/handlers/agent/simple_chat_command.h"
#include "cli/service/agent/simple_chat_session.h"
namespace yaze {
namespace cli {
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;
}
if (auto format = parser.GetString("format")) {
// Simplified format handling
}
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();
}
}
} // namespace handlers
} // namespace cli
} // namespace yaze

View File

@@ -0,0 +1,31 @@
#ifndef YAZE_CLI_HANDLERS_AGENT_SIMPLE_CHAT_COMMAND_H_
#define YAZE_CLI_HANDLERS_AGENT_SIMPLE_CHAT_COMMAND_H_
#include "cli/service/resources/command_handler.h"
namespace yaze {
namespace cli {
namespace handlers {
class SimpleChatCommandHandler : public resources::CommandHandler {
public:
std::string GetName() const { return "simple-chat"; }
std::string GetDescription() const { return "Simple text-based chat with the AI agent."; }
std::string GetUsage() const override {
return "simple-chat [--prompt <message>] [--file <path>] [--format <format>]";
}
protected:
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
return absl::OkStatus();
}
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
resources::OutputFormatter& formatter) override;
};
} // namespace handlers
} // namespace cli
} // namespace yaze
#endif // YAZE_CLI_HANDLERS_AGENT_SIMPLE_CHAT_COMMAND_H_

View File

@@ -60,9 +60,14 @@ std::vector<std::unique_ptr<resources::CommandHandler>> CreateCliCommandHandlers
return handlers;
}
#include "cli/handlers/agent/simple_chat_command.h"
std::vector<std::unique_ptr<resources::CommandHandler>> CreateAgentCommandHandlers() {
std::vector<std::unique_ptr<resources::CommandHandler>> handlers;
// Add simple-chat command handler
handlers.push_back(std::make_unique<SimpleChatCommandHandler>());
// Resource inspection tools
handlers.push_back(std::make_unique<ResourceListCommandHandler>());
handlers.push_back(std::make_unique<ResourceSearchCommandHandler>());

View File

@@ -7,6 +7,8 @@
#include "cli/service/resources/command_handler.h"
#include "cli/handlers/agent/simple_chat_command.h"
namespace yaze {
namespace cli {
namespace handlers {

View File

@@ -1,328 +0,0 @@
#include "cli/handlers/commands.h"
#include "cli/handlers/tools/resource_commands.h"
#include "cli/handlers/game/dungeon_commands.h"
#include "cli/handlers/game/overworld_commands.h"
#include "cli/handlers/game/message_commands.h"
#include "cli/handlers/game/dialogue_commands.h"
#include "cli/handlers/game/music_commands.h"
#include "cli/handlers/graphics/hex_commands.h"
#include "cli/handlers/graphics/palette_commands.h"
// #include "cli/handlers/graphics/sprite_commands.h" // Implementations not available
#include "cli/handlers/tools/gui_commands.h"
#include "cli/handlers/tools/emulator_commands.h"
// #include "cli/handlers/rom/rom_commands.h" // Not used in stubs
// #include "cli/handlers/rom/project_commands.h" // Not used in stubs
namespace yaze {
namespace cli {
namespace handlers {
// Resource commands
absl::Status HandleResourceListCommand(const std::vector<std::string>& args, Rom* rom) {
ResourceListCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleResourceSearchCommand(const std::vector<std::string>& args, Rom* rom) {
ResourceSearchCommandHandler handler;
return handler.Run(args, rom);
}
// Dungeon commands
absl::Status HandleDungeonListSpritesCommand(const std::vector<std::string>& args, Rom* rom) {
DungeonListSpritesCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDungeonDescribeRoomCommand(const std::vector<std::string>& args, Rom* rom) {
DungeonDescribeRoomCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDungeonExportRoomCommand(const std::vector<std::string>& args, Rom* rom) {
DungeonExportRoomCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDungeonListObjectsCommand(const std::vector<std::string>& args, Rom* rom) {
DungeonListObjectsCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDungeonGetRoomTilesCommand(const std::vector<std::string>& args, Rom* rom) {
DungeonGetRoomTilesCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDungeonSetRoomPropertyCommand(const std::vector<std::string>& args, Rom* rom) {
DungeonSetRoomPropertyCommandHandler handler;
return handler.Run(args, rom);
}
// Overworld commands
absl::Status HandleOverworldFindTileCommand(const std::vector<std::string>& args, Rom* rom) {
OverworldFindTileCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleOverworldDescribeMapCommand(const std::vector<std::string>& args, Rom* rom) {
OverworldDescribeMapCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleOverworldListWarpsCommand(const std::vector<std::string>& args, Rom* rom) {
OverworldListWarpsCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleOverworldListSpritesCommand(const std::vector<std::string>& args, Rom* rom) {
OverworldListSpritesCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleOverworldGetEntranceCommand(const std::vector<std::string>& args, Rom* rom) {
OverworldGetEntranceCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleOverworldTileStatsCommand(const std::vector<std::string>& args, Rom* rom) {
OverworldTileStatsCommandHandler handler;
return handler.Run(args, rom);
}
// Message commands
absl::Status HandleMessageListCommand(const std::vector<std::string>& args, Rom* rom) {
MessageListCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleMessageReadCommand(const std::vector<std::string>& args, Rom* rom) {
MessageReadCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleMessageSearchCommand(const std::vector<std::string>& args, Rom* rom) {
MessageSearchCommandHandler handler;
return handler.Run(args, rom);
}
// Dialogue commands
absl::Status HandleDialogueListCommand(const std::vector<std::string>& args, Rom* rom) {
DialogueListCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDialogueReadCommand(const std::vector<std::string>& args, Rom* rom) {
DialogueReadCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleDialogueSearchCommand(const std::vector<std::string>& args, Rom* rom) {
DialogueSearchCommandHandler handler;
return handler.Run(args, rom);
}
// Music commands
absl::Status HandleMusicListCommand(const std::vector<std::string>& args, Rom* rom) {
MusicListCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleMusicInfoCommand(const std::vector<std::string>& args, Rom* rom) {
MusicInfoCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleMusicTracksCommand(const std::vector<std::string>& args, Rom* rom) {
MusicTracksCommandHandler handler;
return handler.Run(args, rom);
}
// Sprite commands (stubs - implementations not available)
absl::Status HandleSpriteListCommand(const std::vector<std::string>& /*args*/, Rom* /*rom*/) {
return absl::OkStatus();
}
absl::Status HandleSpritePropertiesCommand(const std::vector<std::string>& /*args*/, Rom* /*rom*/) {
return absl::OkStatus();
}
absl::Status HandleSpritePaletteCommand(const std::vector<std::string>& /*args*/, Rom* /*rom*/) {
return absl::OkStatus();
}
// GUI commands
absl::Status HandleGuiPlaceTileCommand(const std::vector<std::string>& args, Rom* rom) {
GuiPlaceTileCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleGuiClickCommand(const std::vector<std::string>& args, Rom* rom) {
GuiClickCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleGuiDiscoverToolCommand(const std::vector<std::string>& args, Rom* rom) {
GuiDiscoverToolCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleGuiScreenshotCommand(const std::vector<std::string>& args, Rom* rom) {
GuiScreenshotCommandHandler handler;
return handler.Run(args, rom);
}
// Emulator commands
absl::Status HandleEmulatorStepCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorStepCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorRunCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorRunCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorPauseCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorPauseCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorResetCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorResetCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorGetStateCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorGetStateCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorSetBreakpointCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorSetBreakpointCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorClearBreakpointCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorClearBreakpointCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorListBreakpointsCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorListBreakpointsCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorReadMemoryCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorReadMemoryCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorWriteMemoryCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorWriteMemoryCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorGetRegistersCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorGetRegistersCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleEmulatorGetMetricsCommand(const std::vector<std::string>& args, Rom* rom) {
EmulatorGetMetricsCommandHandler handler;
return handler.Run(args, rom);
}
// Hex commands
absl::Status HandleHexRead(const std::vector<std::string>& args, Rom* rom) {
HexReadCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleHexWrite(const std::vector<std::string>& args, Rom* rom) {
HexWriteCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandleHexSearch(const std::vector<std::string>& args, Rom* rom) {
HexSearchCommandHandler handler;
return handler.Run(args, rom);
}
// Palette commands
absl::Status HandlePaletteGetColors(const std::vector<std::string>& args, Rom* rom) {
PaletteGetColorsCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandlePaletteSetColor(const std::vector<std::string>& args, Rom* rom) {
PaletteSetColorCommandHandler handler;
return handler.Run(args, rom);
}
absl::Status HandlePaletteAnalyze(const std::vector<std::string>& args, Rom* rom) {
PaletteAnalyzeCommandHandler handler;
return handler.Run(args, rom);
}
// Agent-specific commands (stubs for now)
absl::Status HandleRunCommand(const std::vector<std::string>& /*args*/, Rom& /*rom*/) {
return absl::OkStatus();
}
absl::Status HandlePlanCommand(const std::vector<std::string>& /*args*/) {
return absl::OkStatus();
}
absl::Status HandleDiffCommand(Rom& /*rom*/, const std::vector<std::string>& /*args*/) {
return absl::OkStatus();
}
absl::Status HandleAcceptCommand(const std::vector<std::string>& /*args*/, Rom& /*rom*/) {
return absl::OkStatus();
}
absl::Status HandleTestCommand(const std::vector<std::string>& /*args*/) {
return absl::OkStatus();
}
absl::Status HandleGuiCommand(const std::vector<std::string>& /*args*/) {
return absl::OkStatus();
}
absl::Status HandleLearnCommand(const std::vector<std::string>& /*args*/) {
return absl::OkStatus();
}
absl::Status HandleListCommand() {
return absl::OkStatus();
}
absl::Status HandleCommitCommand(Rom& /*rom*/) {
return absl::OkStatus();
}
absl::Status HandleRevertCommand(Rom& /*rom*/) {
return absl::OkStatus();
}
absl::Status HandleDescribeCommand(const std::vector<std::string>& /*arg_vec*/) {
return absl::OkStatus();
}
absl::Status HandleChatCommand(Rom& /*rom*/) {
return absl::OkStatus();
}
absl::Status HandleSimpleChatCommand(const std::vector<std::string>&, Rom* /*rom*/, bool /*quiet*/) {
return absl::OkStatus();
}
absl::Status HandleTestConversationCommand(const std::vector<std::string>& /*arg_vec*/) {
return absl::OkStatus();
}
} // namespace handlers
} // namespace cli
} // namespace yaze

View File

@@ -1,194 +0,0 @@
#ifndef YAZE_CLI_HANDLERS_COMMANDS_H_
#define YAZE_CLI_HANDLERS_COMMANDS_H_
#include <string>
#include <vector>
#include "absl/status/status.h"
namespace yaze {
class Rom;
namespace cli {
namespace handlers {
absl::Status HandleRunCommand(const std::vector<std::string>& args,
Rom& rom);
absl::Status HandlePlanCommand(const std::vector<std::string>& args);
absl::Status HandleDiffCommand(Rom& rom,
const std::vector<std::string>& args);
absl::Status HandleAcceptCommand(const std::vector<std::string>& args, Rom& rom);
absl::Status HandleTestCommand(const std::vector<std::string>& args);
absl::Status HandleGuiCommand(const std::vector<std::string>& args);
absl::Status HandleLearnCommand(const std::vector<std::string>& args = {});
absl::Status HandleListCommand();
absl::Status HandleCommitCommand(Rom& rom);
absl::Status HandleRevertCommand(Rom& rom);
absl::Status HandleDescribeCommand(const std::vector<std::string>& arg_vec);
absl::Status HandleResourceListCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleResourceSearchCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDungeonListSpritesCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDungeonDescribeRoomCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleOverworldFindTileCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleOverworldDescribeMapCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleOverworldListWarpsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleOverworldListSpritesCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleOverworldGetEntranceCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleOverworldTileStatsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleMessageListCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleMessageReadCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleMessageSearchCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// GUI Automation Tools
absl::Status HandleGuiPlaceTileCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleGuiClickCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleGuiDiscoverToolCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleGuiScreenshotCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// Dialogue Inspection Tools
absl::Status HandleDialogueListCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDialogueReadCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDialogueSearchCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// Music Data Tools
absl::Status HandleMusicListCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleMusicInfoCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleMusicTracksCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// Sprite Property Tools
absl::Status HandleSpriteListCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleSpritePropertiesCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleSpritePaletteCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleChatCommand(Rom& rom);
absl::Status HandleSimpleChatCommand(const std::vector<std::string>&, Rom* rom, bool quiet);
absl::Status HandleTestConversationCommand(
const std::vector<std::string>& arg_vec);
// Agent command handler
absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec);
// Hex manipulation commands
absl::Status HandleHexRead(const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleHexWrite(const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleHexSearch(const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// Palette manipulation commands
absl::Status HandlePaletteGetColors(const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandlePaletteSetColor(const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandlePaletteAnalyze(const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// Dungeon editing commands
absl::Status HandleDungeonExportRoomCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDungeonListObjectsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDungeonGetRoomTilesCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDungeonSetRoomPropertyCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// Emulator & Debugger commands
absl::Status HandleEmulatorStepCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorRunCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorPauseCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorResetCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorGetStateCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorSetBreakpointCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorClearBreakpointCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorListBreakpointsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorReadMemoryCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorWriteMemoryCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorGetRegistersCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorGetMetricsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
} // namespace handlers
} // namespace cli
} // namespace yaze
#endif // YAZE_CLI_HANDLERS_COMMANDS_H_

View File

@@ -183,6 +183,7 @@ ConversationalAgentService::ConversationalAgentService(const AgentConfig& config
void ConversationalAgentService::SetRomContext(Rom* rom) {
rom_context_ = rom;
tool_dispatcher_.SetRomContext(rom_context_);
if (ai_service_) {
ai_service_->SetRomContext(rom_context_);

View File

@@ -6,11 +6,12 @@
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/time/time.h"
#include "cli/service/ai/ai_service.h"
#include "cli/service/agent/tool_dispatcher.h"
#include "cli/service/agent/proposal_executor.h"
#include "cli/service/agent/tool_dispatcher.h"
namespace yaze {

View File

@@ -1,139 +1,282 @@
#include "cli/service/agent/tool_dispatcher.h"
#include <iostream>
#include <algorithm>
#include <memory>
#include <regex>
#include <sstream>
#include <string>
#include "absl/strings/match.h"
#include "absl/strings/str_format.h"
#include "cli/handlers/commands.h"
#include "absl/strings/str_cat.h"
#include "cli/handlers/game/dialogue_commands.h"
#include "cli/handlers/game/dungeon_commands.h"
#include "cli/handlers/game/message_commands.h"
#include "cli/handlers/game/music_commands.h"
#include "cli/handlers/game/overworld_commands.h"
#include "cli/handlers/graphics/sprite_commands.h"
#include "cli/handlers/tools/emulator_commands.h"
#include "cli/handlers/tools/gui_commands.h"
#include "cli/handlers/tools/resource_commands.h"
#include "cli/service/resources/command_context.h"
#include "cli/util/terminal_colors.h"
namespace yaze {
namespace cli {
namespace agent {
absl::StatusOr<std::string> ToolDispatcher::Dispatch(
const ToolCall& tool_call) {
namespace {
// Map tool name to handler type
ToolCallType GetToolCallType(const std::string& tool_name) {
// Resource commands
if (tool_name == "resource-list") return ToolCallType::kResourceList;
if (tool_name == "resource-search") return ToolCallType::kResourceSearch;
// Dungeon commands
if (tool_name == "dungeon-list-sprites") return ToolCallType::kDungeonListSprites;
if (tool_name == "dungeon-describe-room") return ToolCallType::kDungeonDescribeRoom;
if (tool_name == "dungeon-export-room") return ToolCallType::kDungeonExportRoom;
if (tool_name == "dungeon-list-objects") return ToolCallType::kDungeonListObjects;
if (tool_name == "dungeon-get-room-tiles") return ToolCallType::kDungeonGetRoomTiles;
if (tool_name == "dungeon-set-room-property") return ToolCallType::kDungeonSetRoomProperty;
// Overworld commands
if (tool_name == "overworld-find-tile") return ToolCallType::kOverworldFindTile;
if (tool_name == "overworld-describe-map") return ToolCallType::kOverworldDescribeMap;
if (tool_name == "overworld-list-warps") return ToolCallType::kOverworldListWarps;
if (tool_name == "overworld-list-sprites") return ToolCallType::kOverworldListSprites;
if (tool_name == "overworld-get-entrance") return ToolCallType::kOverworldGetEntrance;
if (tool_name == "overworld-tile-stats") return ToolCallType::kOverworldTileStats;
// Message & Dialogue commands
if (tool_name == "message-list") return ToolCallType::kMessageList;
if (tool_name == "message-read") return ToolCallType::kMessageRead;
if (tool_name == "message-search") return ToolCallType::kMessageSearch;
if (tool_name == "dialogue-list") return ToolCallType::kDialogueList;
if (tool_name == "dialogue-read") return ToolCallType::kDialogueRead;
if (tool_name == "dialogue-search") return ToolCallType::kDialogueSearch;
// GUI Automation commands
if (tool_name == "gui-place-tile") return ToolCallType::kGuiPlaceTile;
if (tool_name == "gui-click") return ToolCallType::kGuiClick;
if (tool_name == "gui-discover-tool") return ToolCallType::kGuiDiscover;
if (tool_name == "gui-screenshot") return ToolCallType::kGuiScreenshot;
// Music commands
if (tool_name == "music-list") return ToolCallType::kMusicList;
if (tool_name == "music-info") return ToolCallType::kMusicInfo;
if (tool_name == "music-tracks") return ToolCallType::kMusicTracks;
// Sprite commands
if (tool_name == "sprite-list") return ToolCallType::kSpriteList;
if (tool_name == "sprite-properties") return ToolCallType::kSpriteProperties;
if (tool_name == "sprite-palette") return ToolCallType::kSpritePalette;
// Emulator & Debugger commands
if (tool_name == "emulator-step") return ToolCallType::kEmulatorStep;
if (tool_name == "emulator-run") return ToolCallType::kEmulatorRun;
if (tool_name == "emulator-pause") return ToolCallType::kEmulatorPause;
if (tool_name == "emulator-reset") return ToolCallType::kEmulatorReset;
if (tool_name == "emulator-get-state") return ToolCallType::kEmulatorGetState;
if (tool_name == "emulator-set-breakpoint") return ToolCallType::kEmulatorSetBreakpoint;
if (tool_name == "emulator-clear-breakpoint") return ToolCallType::kEmulatorClearBreakpoint;
if (tool_name == "emulator-list-breakpoints") return ToolCallType::kEmulatorListBreakpoints;
if (tool_name == "emulator-read-memory") return ToolCallType::kEmulatorReadMemory;
if (tool_name == "emulator-write-memory") return ToolCallType::kEmulatorWriteMemory;
if (tool_name == "emulator-get-registers") return ToolCallType::kEmulatorGetRegisters;
if (tool_name == "emulator-get-metrics") return ToolCallType::kEmulatorGetMetrics;
return ToolCallType::kUnknown;
}
// Create the appropriate command handler for a tool call type
std::unique_ptr<resources::CommandHandler> CreateHandler(ToolCallType type) {
using namespace yaze::cli::handlers;
std::vector<std::string> args;
switch (type) {
// Resource commands
case ToolCallType::kResourceList:
return std::make_unique<ResourceListCommandHandler>();
case ToolCallType::kResourceSearch:
return std::make_unique<ResourceSearchCommandHandler>();
// Dungeon commands
case ToolCallType::kDungeonListSprites:
return std::make_unique<DungeonListSpritesCommandHandler>();
case ToolCallType::kDungeonDescribeRoom:
return std::make_unique<DungeonDescribeRoomCommandHandler>();
case ToolCallType::kDungeonExportRoom:
return std::make_unique<DungeonExportRoomCommandHandler>();
case ToolCallType::kDungeonListObjects:
return std::make_unique<DungeonListObjectsCommandHandler>();
case ToolCallType::kDungeonGetRoomTiles:
return std::make_unique<DungeonGetRoomTilesCommandHandler>();
case ToolCallType::kDungeonSetRoomProperty:
return std::make_unique<DungeonSetRoomPropertyCommandHandler>();
// Overworld commands
case ToolCallType::kOverworldFindTile:
return std::make_unique<OverworldFindTileCommandHandler>();
case ToolCallType::kOverworldDescribeMap:
return std::make_unique<OverworldDescribeMapCommandHandler>();
case ToolCallType::kOverworldListWarps:
return std::make_unique<OverworldListWarpsCommandHandler>();
case ToolCallType::kOverworldListSprites:
return std::make_unique<OverworldListSpritesCommandHandler>();
case ToolCallType::kOverworldGetEntrance:
return std::make_unique<OverworldGetEntranceCommandHandler>();
case ToolCallType::kOverworldTileStats:
return std::make_unique<OverworldTileStatsCommandHandler>();
// Message & Dialogue commands
case ToolCallType::kMessageList:
return std::make_unique<MessageListCommandHandler>();
case ToolCallType::kMessageRead:
return std::make_unique<MessageReadCommandHandler>();
case ToolCallType::kMessageSearch:
return std::make_unique<MessageSearchCommandHandler>();
case ToolCallType::kDialogueList:
return std::make_unique<DialogueListCommandHandler>();
case ToolCallType::kDialogueRead:
return std::make_unique<DialogueReadCommandHandler>();
case ToolCallType::kDialogueSearch:
return std::make_unique<DialogueSearchCommandHandler>();
// GUI Automation commands
case ToolCallType::kGuiPlaceTile:
return std::make_unique<GuiPlaceTileCommandHandler>();
case ToolCallType::kGuiClick:
return std::make_unique<GuiClickCommandHandler>();
case ToolCallType::kGuiDiscover:
return std::make_unique<GuiDiscoverToolCommandHandler>();
case ToolCallType::kGuiScreenshot:
return std::make_unique<GuiScreenshotCommandHandler>();
// Music commands
case ToolCallType::kMusicList:
return std::make_unique<MusicListCommandHandler>();
case ToolCallType::kMusicInfo:
return std::make_unique<MusicInfoCommandHandler>();
case ToolCallType::kMusicTracks:
return std::make_unique<MusicTracksCommandHandler>();
// Sprite commands
case ToolCallType::kSpriteList:
return std::make_unique<SpriteListCommandHandler>();
case ToolCallType::kSpriteProperties:
return std::make_unique<SpritePropertiesCommandHandler>();
case ToolCallType::kSpritePalette:
return std::make_unique<SpritePaletteCommandHandler>();
// Emulator & Debugger commands
case ToolCallType::kEmulatorStep:
return std::make_unique<EmulatorStepCommandHandler>();
case ToolCallType::kEmulatorRun:
return std::make_unique<EmulatorRunCommandHandler>();
case ToolCallType::kEmulatorPause:
return std::make_unique<EmulatorPauseCommandHandler>();
case ToolCallType::kEmulatorReset:
return std::make_unique<EmulatorResetCommandHandler>();
case ToolCallType::kEmulatorGetState:
return std::make_unique<EmulatorGetStateCommandHandler>();
case ToolCallType::kEmulatorSetBreakpoint:
return std::make_unique<EmulatorSetBreakpointCommandHandler>();
case ToolCallType::kEmulatorClearBreakpoint:
return std::make_unique<EmulatorClearBreakpointCommandHandler>();
case ToolCallType::kEmulatorListBreakpoints:
return std::make_unique<EmulatorListBreakpointsCommandHandler>();
case ToolCallType::kEmulatorReadMemory:
return std::make_unique<EmulatorReadMemoryCommandHandler>();
case ToolCallType::kEmulatorWriteMemory:
return std::make_unique<EmulatorWriteMemoryCommandHandler>();
case ToolCallType::kEmulatorGetRegisters:
return std::make_unique<EmulatorGetRegistersCommandHandler>();
case ToolCallType::kEmulatorGetMetrics:
return std::make_unique<EmulatorGetMetricsCommandHandler>();
default:
return nullptr;
}
}
// Convert tool call arguments map to command-line style vector
std::vector<std::string> ConvertArgsToVector(
const std::map<std::string, std::string>& args) {
std::vector<std::string> result;
for (const auto& [key, value] : args) {
// Convert to --key=value format
result.push_back(absl::StrCat("--", key, "=", value));
}
// Always request JSON format for tool calls (easier for AI to parse)
bool has_format = false;
for (const auto& [key, value] : tool_call.args) {
args.push_back(absl::StrFormat("--%s", key));
args.push_back(value);
if (absl::EqualsIgnoreCase(key, "format")) {
for (const auto& arg : result) {
if (arg.find("--format=") == 0) {
has_format = true;
break;
}
}
if (!has_format) {
args.push_back("--format");
args.push_back("json");
result.push_back("--format=json");
}
return result;
}
// Capture stdout
std::stringstream buffer;
auto old_cout_buf = std::cout.rdbuf();
std::cout.rdbuf(buffer.rdbuf());
} // namespace
absl::Status status;
if (tool_call.tool_name == "resource-list") {
status = HandleResourceListCommand(args, rom_context_);
} else if (tool_call.tool_name == "resource-search") {
status = HandleResourceSearchCommand(args, rom_context_);
} else if (tool_call.tool_name == "dungeon-list-sprites") {
status = HandleDungeonListSpritesCommand(args, rom_context_);
} else if (tool_call.tool_name == "dungeon-describe-room") {
status = HandleDungeonDescribeRoomCommand(args, rom_context_);
} else if (tool_call.tool_name == "overworld-find-tile") {
status = HandleOverworldFindTileCommand(args, rom_context_);
} else if (tool_call.tool_name == "overworld-describe-map") {
status = HandleOverworldDescribeMapCommand(args, rom_context_);
} else if (tool_call.tool_name == "overworld-list-warps") {
status = HandleOverworldListWarpsCommand(args, rom_context_);
} else if (tool_call.tool_name == "overworld-list-sprites") {
status = HandleOverworldListSpritesCommand(args, rom_context_);
} else if (tool_call.tool_name == "overworld-get-entrance") {
status = HandleOverworldGetEntranceCommand(args, rom_context_);
} else if (tool_call.tool_name == "overworld-tile-stats") {
status = HandleOverworldTileStatsCommand(args, rom_context_);
} else if (tool_call.tool_name == "message-list") {
status = HandleMessageListCommand(args, rom_context_);
} else if (tool_call.tool_name == "message-read") {
status = HandleMessageReadCommand(args, rom_context_);
} else if (tool_call.tool_name == "message-search") {
status = HandleMessageSearchCommand(args, rom_context_);
} else if (tool_call.tool_name == "gui-place-tile") {
// GUI automation tool for placing tiles via test harness
status = HandleGuiPlaceTileCommand(args, rom_context_);
} else if (tool_call.tool_name == "gui-click") {
status = HandleGuiClickCommand(args, rom_context_);
} else if (tool_call.tool_name == "gui-discover") {
status = HandleGuiDiscoverToolCommand(args, rom_context_);
} else if (tool_call.tool_name == "gui-screenshot") {
status = HandleGuiScreenshotCommand(args, rom_context_);
} else if (tool_call.tool_name == "dialogue-list") {
status = HandleDialogueListCommand(args, rom_context_);
} else if (tool_call.tool_name == "dialogue-read") {
status = HandleDialogueReadCommand(args, rom_context_);
} else if (tool_call.tool_name == "dialogue-search") {
status = HandleDialogueSearchCommand(args, rom_context_);
} else if (tool_call.tool_name == "music-list") {
status = HandleMusicListCommand(args, rom_context_);
} else if (tool_call.tool_name == "music-info") {
status = HandleMusicInfoCommand(args, rom_context_);
} else if (tool_call.tool_name == "music-tracks") {
status = HandleMusicTracksCommand(args, rom_context_);
} else if (tool_call.tool_name == "sprite-list") {
status = HandleSpriteListCommand(args, rom_context_);
} else if (tool_call.tool_name == "sprite-properties") {
status = HandleSpritePropertiesCommand(args, rom_context_);
} else if (tool_call.tool_name == "sprite-palette") {
status = HandleSpritePaletteCommand(args, rom_context_);
} else if (tool_call.tool_name == "dungeon-export-room") {
status = HandleDungeonExportRoomCommand(args, rom_context_);
} else if (tool_call.tool_name == "dungeon-list-objects") {
status = HandleDungeonListObjectsCommand(args, rom_context_);
} else if (tool_call.tool_name == "dungeon-get-room-tiles") {
status = HandleDungeonGetRoomTilesCommand(args, rom_context_);
} else if (tool_call.tool_name == "dungeon-set-room-property") {
status = HandleDungeonSetRoomPropertyCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-step") {
status = HandleEmulatorStepCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-run") {
status = HandleEmulatorRunCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-pause") {
status = HandleEmulatorPauseCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-reset") {
status = HandleEmulatorResetCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-get-state") {
status = HandleEmulatorGetStateCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-set-breakpoint") {
status = HandleEmulatorSetBreakpointCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-clear-breakpoint") {
status = HandleEmulatorClearBreakpointCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-list-breakpoints") {
status = HandleEmulatorListBreakpointsCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-read-memory") {
status = HandleEmulatorReadMemoryCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-write-memory") {
status = HandleEmulatorWriteMemoryCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-get-registers") {
status = HandleEmulatorGetRegistersCommand(args, rom_context_);
} else if (tool_call.tool_name == "emulator-get-metrics") {
status = HandleEmulatorGetMetricsCommand(args, rom_context_);
} else {
status = absl::UnimplementedError(
absl::StrFormat("Unknown tool: %s", tool_call.tool_name));
absl::StatusOr<std::string> ToolDispatcher::Dispatch(const ToolCall& call) {
// Determine tool call type
ToolCallType type = GetToolCallType(call.tool_name);
if (type == ToolCallType::kUnknown) {
return absl::InvalidArgumentError(
absl::StrCat("Unknown tool: ", call.tool_name));
}
// Create the appropriate command handler
auto handler = CreateHandler(type);
if (!handler) {
return absl::InternalError(
absl::StrCat("Failed to create handler for tool: ", call.tool_name));
}
// Convert arguments to command-line style
std::vector<std::string> args = ConvertArgsToVector(call.args);
// Check if ROM context is required but not available
if (!rom_context_) {
return absl::FailedPreconditionError(
absl::StrCat("Tool '", call.tool_name,
"' requires ROM context but none is available"));
}
// Execute the command handler
// The handler will internally use OutputFormatter to capture output
// We need to capture stdout to get the formatted output
// Redirect stdout to capture the output
std::stringstream output_buffer;
std::streambuf* old_cout = std::cout.rdbuf(output_buffer.rdbuf());
// Execute the handler
absl::Status status = handler->Run(args, rom_context_);
// Restore stdout
std::cout.rdbuf(old_cout_buf);
std::cout.rdbuf(old_cout);
if (!status.ok()) {
return status;
}
return buffer.str();
// Return the captured output
std::string output = output_buffer.str();
if (output.empty()) {
return absl::InternalError(
absl::StrCat("Tool '", call.tool_name, "' produced no output"));
}
return output;
}
} // namespace agent

View File

@@ -1,5 +1,23 @@
#include "cli/service/resources/command_handler.h"
namespace yaze {
namespace cli {
namespace resources {
CommandHandler::Descriptor CommandHandler::Describe() const {
Descriptor descriptor;
descriptor.display_name = GetUsage();
descriptor.summary = "Command summary not provided.";
descriptor.todo_reference = "todo#unassigned";
return descriptor;
}
} // namespace resources
} // namespace cli
} // namespace yaze
#include "cli/service/resources/command_handler.h"
#include <iostream>
#include "absl/strings/str_format.h"

View File

@@ -44,6 +44,19 @@ class CommandHandler {
public:
virtual ~CommandHandler() = default;
struct DescriptorEntry {
std::string name;
std::string description;
std::string todo_reference;
};
struct Descriptor {
std::string display_name;
std::string summary;
std::string todo_reference;
std::vector<DescriptorEntry> entries;
};
/**
* @brief Execute the command
*
@@ -56,6 +69,19 @@ class CommandHandler {
*/
absl::Status Run(const std::vector<std::string>& args, Rom* rom_context);
/**
* @brief Get the command name
*
* Override this to provide a unique identifier for the command.
* This is used for command registration and lookup.
*/
virtual std::string GetName() const = 0;
/**
* @brief Provide metadata for TUI/help summaries.
*/
virtual Descriptor Describe() const;
protected:
/**
* @brief Validate command arguments

View File

@@ -1,9 +1,13 @@
#include "cli/tui/chat_tui.h"
#include <algorithm>
#include <cmath>
#include <utility>
#include <vector>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/time/time.h"
#include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp"
#include "ftxui/component/event.hpp"
@@ -12,6 +16,7 @@
#include "ftxui/dom/table.hpp"
#include "app/rom.h"
#include "cli/tui/autocomplete_ui.h"
#include "cli/tui/tui.h"
namespace yaze {
namespace cli {
@@ -19,6 +24,44 @@ namespace tui {
using namespace ftxui;
namespace {
const std::vector<std::string> kSpinnerFrames = {
"", "", "", "", "", "", "", "", "", ""};
Element RenderPanelCard(const std::string& title, const std::vector<Element>& body,
Color border_color, bool highlight = false) {
auto panel = window(
text(title) | bold,
vbox(body));
if (highlight) {
panel = panel | color(border_color) | bgcolor(Color::GrayDark);
} else {
panel = panel | color(border_color);
}
return panel;
}
Element RenderLatencySparkline(const std::vector<double>& data) {
if (data.empty()) {
return text("No latency data yet") | dim;
}
Elements bars;
for (double d : data) {
bars.push_back(gauge(d) | flex);
}
return hbox(bars);
}
Element RenderMetricLabel(const std::string& icon, const std::string& label,
const std::string& value, Color color) {
return hbox({
text(icon) | ftxui::color(color),
text(" " + label + ": ") | bold,
text(value) | ftxui::color(color)
});
}
} // namespace
ChatTUI::ChatTUI(Rom* rom_context) : rom_context_(rom_context) {
if (rom_context_ != nullptr) {
agent_service_.SetRomContext(rom_context_);
@@ -26,7 +69,19 @@ ChatTUI::ChatTUI(Rom* rom_context) : rom_context_(rom_context) {
} else {
rom_header_ = "No ROM loaded.";
}
auto status = todo_manager_.Initialize();
todo_manager_ready_ = status.ok();
InitializeAutocomplete();
quick_actions_ = {
"List dungeon entrances", "Show sprite palette summary",
"Summarize overworld map", "Find unused rooms",
"Explain ROM header", "Search dialogue for 'Master Sword'",
"Suggest QA checklist", "Show TODO status",
};
}
ChatTUI::~ChatTUI() {
CleanupWorkers();
}
void ChatTUI::SetRomContext(Rom* rom_context) {
@@ -60,15 +115,30 @@ void ChatTUI::Run() {
// Create autocomplete input component
auto input_component = CreateAutocompleteInput(input_message.get(), &autocomplete_engine_);
auto todo_popup_toggle = [this] {
ToggleTodoPopup();
};
auto shortcut_palette_toggle = [this] {
ToggleShortcutPalette();
};
// Handle Enter key BEFORE adding to container
input_component = CatchEvent(input_component, [this, input_message](const Event& event) {
input_component = CatchEvent(input_component, [this, input_message, todo_popup_toggle, shortcut_palette_toggle](const Event& event) {
if (event == Event::Return) {
if (input_message->empty()) return true;
OnSubmit(*input_message);
input_message->clear();
return true;
}
if (event == Event::Special({20})) { // Ctrl+T
todo_popup_toggle();
return true;
}
if (event == Event::Special({11})) { // Ctrl+K
shortcut_palette_toggle();
return true;
}
return false;
});
@@ -78,6 +148,12 @@ void ChatTUI::Run() {
input_message->clear();
});
auto quick_pick_index = std::make_shared<int>(0);
auto quick_pick_menu = Menu(&quick_actions_, quick_pick_index.get());
todo_popup_component_ = CreateTodoPopup();
shortcut_palette_component_ = BuildShortcutPalette();
// Add both input and button to container
auto input_container = Container::Horizontal({
input_component,
@@ -86,7 +162,7 @@ void ChatTUI::Run() {
input_component->TakeFocus();
auto main_renderer = Renderer(input_container, [this, input_component, send_button] {
auto main_renderer = Renderer(input_container, [this, input_component, send_button, quick_pick_menu, quick_pick_index] {
const auto& history = agent_service_.GetHistory();
// Build history view from current history state
@@ -147,24 +223,78 @@ void ChatTUI::Run() {
history_view = vbox(history_elements) | vscroll_indicator | frame | flex;
}
Elements layout_elements = {
text(rom_header_) | bold | center,
separator(),
history_view,
separator(),
};
// Build info panel with responsive layout
auto metrics = CurrentMetrics();
Element header_line = hbox({
text(rom_header_) | bold,
filler(),
agent_busy_.load() ? text(kSpinnerFrames[spinner_index_.load() % kSpinnerFrames.size()]) | color(Color::Yellow)
: text("") | color(Color::GreenLight)
});
std::vector<Element> info_cards;
info_cards.push_back(RenderPanelCard(
"Session",
{
RenderMetricLabel("🕒", "Turns", absl::StrFormat("%d", metrics.turn_index), Color::Cyan),
RenderMetricLabel("🙋", "User", absl::StrFormat("%d", metrics.total_user_messages), Color::White),
RenderMetricLabel("🤖", "Agent", absl::StrFormat("%d", metrics.total_agent_messages), Color::GreenLight),
RenderMetricLabel("🔧", "Tools", absl::StrFormat("%d", metrics.total_tool_calls), Color::YellowLight),
}, Color::GrayLight));
info_cards.push_back(RenderPanelCard(
"Latency",
{
RenderMetricLabel("", "Last", absl::StrFormat("%.2fs", last_response_seconds_), Color::Yellow),
RenderMetricLabel("📈", "Average", absl::StrFormat("%.2fs", metrics.average_latency_seconds), Color::MagentaLight),
RenderLatencySparkline(latency_history_)
}, Color::Magenta, agent_busy_.load()));
info_cards.push_back(RenderPanelCard(
"Shortcuts",
{
text("⌨ Enter ↵ Send") | dim,
text("⌨ Shift+Enter ↩ Multiline") | dim,
text("⌨ /help, /rom_info, /status") | dim,
text("⌨ Ctrl+T TODO overlay · Ctrl+K shortcuts · f fullscreen") | dim,
}, Color::BlueLight));
Elements layout_elements;
layout_elements.push_back(header_line);
layout_elements.push_back(separatorLight());
layout_elements.push_back(
vbox({
hbox({
info_cards[0] | flex,
separator(),
info_cards[1] | flex,
separator(),
info_cards[2] | flex,
}) | flex,
separator(),
history_view |
bgcolor(Color::Black) |
flex
}) | flex);
// Add metrics bar
const auto metrics = agent_service_.GetMetrics();
layout_elements.push_back(separatorLight());
layout_elements.push_back(
hbox({
text(absl::StrFormat("Turns:%d", metrics.turn_index)),
text("Turns: ") | bold,
text(absl::StrFormat("%d", metrics.turn_index)),
separator(),
text(absl::StrFormat("User:%d", metrics.total_user_messages)),
text("User: ") | bold,
text(absl::StrFormat("%d", metrics.total_user_messages)),
separator(),
text(absl::StrFormat("Agent:%d", metrics.total_agent_messages)),
text("Agent: ") | bold,
text(absl::StrFormat("%d", metrics.total_agent_messages)),
separator(),
text(absl::StrFormat("Tools:%d", metrics.total_tool_calls)),
text("Tools: ") | bold,
text(absl::StrFormat("%d", metrics.total_tool_calls)),
filler(),
text("Last response " + absl::StrFormat("%.2fs", last_response_seconds_)) | color(Color::GrayLight)
}) | color(Color::GrayLight)
);
@@ -179,17 +309,38 @@ void ChatTUI::Run() {
// Add input area
layout_elements.push_back(separator());
layout_elements.push_back(
input_component->Render() | flex
vbox({
text("Quick Actions") | bold,
quick_pick_menu->Render() | frame | size(HEIGHT, EQUAL, 5) |
flex,
separatorLight(),
input_component->Render() | flex
})
);
layout_elements.push_back(
hbox({
text("Press Enter to send | ") | dim,
send_button->Render(),
text(" | /help for commands") | dim,
text(" | Tab quick actions · Ctrl+T TODO overlay · Ctrl+K shortcuts") | dim,
}) | center
);
return vbox(layout_elements) | border;
Element base = vbox(layout_elements) | borderRounded | bgcolor(Color::Black);
if ((todo_popup_visible_ && todo_popup_component_) ||
(shortcut_palette_visible_ && shortcut_palette_component_)) {
std::vector<Element> overlays;
overlays.push_back(base);
if (todo_popup_visible_ && todo_popup_component_) {
overlays.push_back(todo_popup_component_->Render());
}
if (shortcut_palette_visible_ && shortcut_palette_component_) {
overlays.push_back(shortcut_palette_component_->Render());
}
base = dbox(overlays);
}
return base;
});
screen_.Loop(main_renderer);
@@ -259,12 +410,181 @@ void ChatTUI::OnSubmit(const std::string& message) {
return;
}
auto response = agent_service_.SendMessage(message);
if (!response.ok()) {
last_error_ = response.status().message();
} else {
last_error_.reset();
LaunchAgentPrompt(message);
}
void ChatTUI::LaunchAgentPrompt(const std::string& prompt) {
if (prompt.empty()) {
return;
}
agent_busy_.store(true);
spinner_running_.store(true);
if (!spinner_thread_.joinable()) {
spinner_thread_ = std::thread([this] {
while (spinner_running_.load()) {
std::this_thread::sleep_for(std::chrono::milliseconds(90));
spinner_index_.fetch_add(1);
screen_.PostEvent(Event::Custom);
}
});
}
last_send_time_ = std::chrono::steady_clock::now();
auto future = std::async(std::launch::async, [this, prompt] {
auto response = agent_service_.SendMessage(prompt);
if (!response.ok()) {
last_error_ = response.status().message();
} else {
last_error_.reset();
}
auto end_time = std::chrono::steady_clock::now();
last_response_seconds_ = std::chrono::duration<double>(end_time - last_send_time_).count();
latency_history_.push_back(last_response_seconds_);
if (latency_history_.size() > 30) {
latency_history_.erase(latency_history_.begin());
}
agent_busy_.store(false);
StopSpinner();
screen_.PostEvent(Event::Custom);
});
{
std::lock_guard<std::mutex> lock(worker_mutex_);
worker_futures_.push_back(std::move(future));
}
}
void ChatTUI::CleanupWorkers() {
std::lock_guard<std::mutex> lock(worker_mutex_);
for (auto& future : worker_futures_) {
if (future.valid()) {
future.wait();
}
}
worker_futures_.clear();
StopSpinner();
}
agent::ChatMessage::SessionMetrics ChatTUI::CurrentMetrics() const {
return agent_service_.GetMetrics();
}
void ChatTUI::StopSpinner() {
spinner_running_.store(false);
if (spinner_thread_.joinable()) {
spinner_thread_.join();
}
}
void ChatTUI::ToggleTodoPopup() {
if (!todo_popup_component_) {
todo_popup_component_ = CreateTodoPopup();
}
todo_popup_visible_ = !todo_popup_visible_;
if (todo_popup_visible_ && todo_popup_component_) {
screen_.PostEvent(Event::Custom);
}
}
void ChatTUI::ToggleShortcutPalette() {
if (!shortcut_palette_component_) {
shortcut_palette_component_ = BuildShortcutPalette();
}
shortcut_palette_visible_ = !shortcut_palette_visible_;
if (shortcut_palette_visible_) {
screen_.PostEvent(Event::Custom);
}
}
ftxui::Component ChatTUI::CreateTodoPopup() {
auto refresh_button = Button("Refresh", [this] {
screen_.PostEvent(Event::Custom);
});
auto close_button = Button("Close", [this] {
todo_popup_visible_ = false;
screen_.PostEvent(Event::Custom);
});
auto renderer = Renderer([this, refresh_button, close_button] {
Elements rows;
if (!todo_manager_ready_) {
rows.push_back(text("TODO manager unavailable") | color(Color::Red) | center);
} else {
auto todos = todo_manager_.GetAllTodos();
if (todos.empty()) {
rows.push_back(text("No TODOs tracked") | dim | center);
} else {
for (const auto& item : todos) {
rows.push_back(hbox({
text(absl::StrFormat("[%s]", item.StatusToString())) | color(Color::Yellow),
text(" " + item.description) | flex,
text(item.category.empty() ? "" : absl::StrCat(" (", item.category, ")")) | dim
}));
}
}
}
return dbox({
window(text("📝 TODO Overlay") | bold,
vbox({
separatorLight(),
vbox(rows) | frame | size(HEIGHT, LESS_THAN, 12) | size(WIDTH, LESS_THAN, 70),
separatorLight(),
hbox({refresh_button->Render(), text(" "), close_button->Render()}) | center
}))
| size(WIDTH, LESS_THAN, 72) | size(HEIGHT, LESS_THAN, 18) | center
});
});
return renderer;
}
ftxui::Component ChatTUI::BuildShortcutPalette() {
std::vector<std::pair<std::string, std::string>> shortcuts = {
{"Ctrl+T", "Toggle TODO overlay"},
{"Ctrl+K", "Shortcut palette"},
{"Ctrl+L", "Clear chat history"},
{"Ctrl+Shift+S", "Save transcript"},
{"Ctrl+G", "Focus quick actions"},
{"Ctrl+P", "Command palette"},
{"Ctrl+F", "Fullscreen chat"},
{"Esc", "Back to unified layout"},
};
auto close_button = Button("Close", [this] {
shortcut_palette_visible_ = false;
screen_.PostEvent(Event::Custom);
});
auto renderer = Renderer([shortcuts, close_button] {
Elements rows;
for (const auto& [combo, desc] : shortcuts) {
rows.push_back(hbox({text(combo) | bold | color(Color::Cyan), text(" " + desc)}));
}
return dbox({
window(text("⌨ Shortcuts") | bold,
vbox({
separatorLight(),
vbox(rows) | frame | size(HEIGHT, LESS_THAN, 12) | size(WIDTH, LESS_THAN, 60),
separatorLight(),
close_button->Render() | center
}))
| size(WIDTH, LESS_THAN, 64) | size(HEIGHT, LESS_THAN, 16) | center
});
});
return renderer;
}
bool ChatTUI::IsPopupOpen() const {
return todo_popup_visible_ || shortcut_palette_visible_;
}
} // namespace tui

View File

@@ -1,11 +1,18 @@
#ifndef YAZE_SRC_CLI_TUI_CHAT_TUI_H_
#define YAZE_SRC_CLI_TUI_CHAT_TUI_H_
#include <atomic>
#include <chrono>
#include <future>
#include <mutex>
#include <optional>
#include <thread>
#include <vector>
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "cli/service/agent/conversational_agent_service.h"
#include "cli/service/agent/todo_manager.h"
#include "cli/util/autocomplete.h"
@@ -19,19 +26,54 @@ namespace tui {
class ChatTUI {
public:
explicit ChatTUI(Rom* rom_context = nullptr);
~ChatTUI();
void Run();
void SetRomContext(Rom* rom_context);
private:
void OnSubmit(const std::string& message);
void LaunchAgentPrompt(const std::string& prompt);
void CleanupWorkers();
void StopSpinner();
void InitializeAutocomplete();
agent::ChatMessage::SessionMetrics CurrentMetrics() const;
// Popup state
void ToggleTodoPopup();
ftxui::Component CreateTodoPopup();
ftxui::Component BuildShortcutPalette();
bool IsPopupOpen() const;
void ToggleShortcutPalette();
ftxui::ScreenInteractive screen_ = ftxui::ScreenInteractive::Fullscreen();
agent::ConversationalAgentService agent_service_;
agent::TodoManager todo_manager_;
Rom* rom_context_ = nullptr;
std::optional<std::string> last_error_;
AutocompleteEngine autocomplete_engine_;
std::string rom_header_;
std::atomic<bool> agent_busy_{false};
std::atomic<bool> spinner_running_{false};
std::atomic<int> spinner_index_{0};
std::vector<std::future<void>> worker_futures_;
mutable std::mutex worker_mutex_;
std::chrono::steady_clock::time_point last_send_time_{};
double last_response_seconds_ = 0.0;
std::vector<double> latency_history_;
std::vector<std::string> quick_actions_;
std::thread spinner_thread_;
// Popup state
bool todo_popup_visible_ = false;
ftxui::Component todo_popup_component_;
ftxui::Component shortcut_palette_component_;
bool shortcut_palette_visible_ = false;
bool todo_manager_ready_ = false;
};
} // namespace tui

View File

@@ -4,9 +4,12 @@
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/terminal.hpp>
#include <numeric>
#include <utility>
#include "absl/strings/str_format.h"
#include "cli/service/agent/conversational_agent_service.h"
#include "cli/z3ed_ascii_logo.h"
namespace yaze {
namespace cli {
@@ -27,7 +30,45 @@ UnifiedLayout::UnifiedLayout(Rom* rom_context)
if (rom_context_) {
state_.current_rom_file = rom_context_->title();
}
state_.active_workflows = {
"ROM Audit",
"Dungeon QA",
"Palette Polish"
};
InitializeTheme();
status_provider_ = [this] {
auto rom_loaded = rom_context_ && rom_context_->is_loaded();
return vbox({
text(rom_loaded ? "✅ Ready" : "⚠ Awaiting ROM") |
color(rom_loaded ? Color::GreenLight : Color::YellowLight),
text(absl::StrFormat("Focus: %s",
state_.command_palette_hint.empty() ?
"Main Menu" : state_.command_palette_hint)) |
dim
});
};
command_summary_provider_ = [] {
return std::vector<std::string>{
"agent::chat — conversational ROM inspector",
"rom::info — metadata & validation",
"dungeon::list — dungeon manifest",
"gfx::export — sprite/palette dump",
"project::build — apply patches"
};
};
todo_provider_ = [] {
return std::vector<std::string>{
"[pending] Implement dungeon diff visualizer",
"[pending] Integrate Claude-style context panes",
"[todo] Hook TODO manager into project manifests"
};
};
// Create components
main_menu_panel_ = CreateMainMenuPanel();
chat_panel_ = CreateChatPanel();
@@ -94,10 +135,61 @@ void UnifiedLayout::ToggleStatus() {
screen_.PostEvent(Event::Custom); // Force screen refresh
}
void UnifiedLayout::ToggleTodoOverlay() {
todo_overlay_visible_ = !todo_overlay_visible_;
if (todo_overlay_visible_) {
if (!todo_overlay_component_ && todo_provider_) {
todo_overlay_component_ = Renderer([this] {
Elements rows;
if (todo_provider_) {
auto items = todo_provider_();
if (items.empty()) {
rows.push_back(text("No TODOs available") | dim | center);
} else {
for (const auto& line : items) {
rows.push_back(text(line));
}
}
}
return dbox({window(text("📝 TODO Overlay") | bold,
vbox({
separatorLight(),
vbox(rows) | frame | size(HEIGHT, LESS_THAN, 15) | size(WIDTH, LESS_THAN, 80),
separatorLight(),
text("Ctrl+T to close • Enter to jump via command palette") | dim | center
})) | center});
});
}
screen_.PostEvent(Event::Custom);
} else {
screen_.PostEvent(Event::Custom);
}
}
void UnifiedLayout::SetLayoutConfig(const LayoutConfig& config) {
config_ = config;
}
void UnifiedLayout::SetStatusProvider(std::function<Element()> provider) {
status_provider_ = std::move(provider);
}
void UnifiedLayout::SetCommandSummaryProvider(std::function<std::vector<std::string>()> provider) {
command_summary_provider_ = std::move(provider);
}
void UnifiedLayout::SetTodoProvider(std::function<std::vector<std::string>()> provider) {
todo_provider_ = std::move(provider);
}
void UnifiedLayout::InitializeTheme() {
auto terminal = Terminal::Size();
if (terminal.dimx < 120) {
config_.right_panel_width = 30;
config_.bottom_panel_height = 12;
}
}
Component UnifiedLayout::CreateMainMenuPanel() {
struct MenuState {
int selected = 0;
@@ -133,13 +225,18 @@ Component UnifiedLayout::CreateMainMenuPanel() {
auto menu = Menu(&state->items, &state->selected, option);
return Renderer(menu, [this, menu, state] {
auto banner = RenderAnimatedBanner();
return vbox({
text("🎮 Z3ED Main Menu") | bold | center,
banner,
separator(),
menu->Render(),
separator(),
RenderCommandHints(),
separator(),
RenderWorkflowLane(),
separator(),
text("↑/↓: Navigate | Enter: Select | q: Quit") | dim | center
});
}) | borderRounded | bgcolor(Color::Black);
});
}
@@ -147,26 +244,40 @@ Component UnifiedLayout::CreateChatPanel() {
// Use the full-featured ChatTUI if available
if (chat_tui_) {
return Renderer([this] {
std::vector<Element> cards;
cards.push_back(vbox({
text("🤖 Overview") | bold,
text("Claude-style assistant for ROM editing"),
text("Press 'f' for fullscreen chat") | dim
}) | borderRounded);
if (rom_context_) {
cards.push_back(vbox({
text("📦 ROM Context") | bold,
text(rom_context_->title()),
text(absl::StrFormat("Size: %d bytes", rom_context_->size())) | dim
}) | borderRounded | color(Color::GreenLight));
} else {
cards.push_back(vbox({
text("⚠ No ROM loaded") | color(Color::Yellow),
text("Use Load ROM from main menu") | dim
}) | borderRounded);
}
cards.push_back(vbox({
text("🛠 Integrations") | bold,
text("• TODO manager status") | dim,
text("• Command palette shortcuts") | dim,
text("• Tool dispatcher metrics") | dim
}) | borderRounded);
return vbox({
text("🤖 AI Chat Assistant") | bold | center | color(Color::Cyan),
RenderPanelHeader(PanelType::kChat),
separator(),
text("Press 'f' for full chat | 'c' to toggle") | center | dim,
RenderResponsiveGrid(cards),
separator(),
hbox({
text("Status: ") | bold,
text(rom_context_ ? "ROM Loaded ✓" : "No ROM") |
color(rom_context_ ? Color::Green : Color::Red)
}) | center,
separator(),
text("Features:") | bold | center,
text(" • Natural language ROM queries") | dim,
text(" • Dungeon & overworld inspection") | dim,
text(" • Sprite & palette analysis") | dim,
text(" • Message & dialogue search") | dim,
text(" • Emulator control (when running)") | dim,
separator(),
text("Type '/help' for commands") | center | dim
});
text("Shortcuts: f fullscreen | c toggle chat | /help commands") | dim | center
}) | borderRounded | bgcolor(Color::Black);
});
}
@@ -227,40 +338,32 @@ Component UnifiedLayout::CreateChatPanel() {
Component UnifiedLayout::CreateStatusPanel() {
return Renderer([this] {
std::string rom_info = rom_context_ ?
absl::StrFormat("ROM: %s | Size: %d bytes",
rom_context_->title(), rom_context_->size()) :
"No ROM loaded";
std::string panel_name;
switch (state_.active_main_panel) {
case PanelType::kMainMenu: panel_name = "Main Menu"; break;
case PanelType::kChat: panel_name = "Chat"; break;
case PanelType::kHexViewer: panel_name = "Hex Viewer"; break;
case PanelType::kPaletteEditor: panel_name = "Palette Editor"; break;
case PanelType::kTodoManager: panel_name = "TODO Manager"; break;
case PanelType::kRomTools: panel_name = "ROM Tools"; break;
case PanelType::kGraphicsTools: panel_name = "Graphics Tools"; break;
case PanelType::kSettings: panel_name = "Settings"; break;
case PanelType::kHelp: panel_name = "Help"; break;
default: panel_name = "Other"; break;
Element rom_info = rom_context_ ?
text(absl::StrFormat("ROM: %s", rom_context_->title())) | color(Color::GreenLight) :
text("ROM: none") | color(Color::RedLight);
Element provider_status = status_provider_ ? status_provider_() : text("Ready") | color(Color::GrayLight);
auto command_tiles = RenderCommandHints();
auto todo_tiles = RenderTodoStack();
std::vector<Element> sections = {
RenderAnimatedBanner(),
separatorLight(),
rom_info,
separatorLight(),
provider_status,
separatorLight(),
command_tiles,
separatorLight(),
todo_tiles
};
if (!state_.current_error.empty()) {
sections.push_back(separatorLight());
sections.push_back(text(state_.current_error) | color(Color::Red) | bold);
}
return vbox({
text("📊 Status") | bold | center,
separator(),
text(rom_info) | color(rom_context_ ? Color::Green : Color::Red),
separator(),
text("Panel: ") | bold,
text(panel_name),
separator(),
text("Layout: ") | bold,
text(absl::StrFormat("Chat: %s | Status: %s",
config_.show_chat ? "ON" : "OFF",
config_.show_status ? "ON" : "OFF")),
separator(),
text("Press 'h' for help, 'q' to quit") | dim | center
});
return vbox(sections) | borderRounded | bgcolor(Color::Black);
});
}
@@ -268,25 +371,35 @@ Component UnifiedLayout::CreateToolsPanel() {
struct ToolsState {
int selected = 0;
std::vector<std::string> items = {
"🔧 ROM Tools",
"🎨 Graphics Tools",
"📝 TODO Manager",
"🔧 ROM Tools (press t)",
"🎨 Graphics Tools (ref gfx::export)",
"📝 TODO Manager (ref todo::list)",
"⚙️ Settings",
"❓ Help"
};
};
auto state = std::make_shared<ToolsState>();
auto menu = Menu(&state->items, &state->selected);
MenuOption option;
option.on_change = [this, state] {
if (!state->items.empty()) {
state_.command_palette_hint = state->items[state->selected];
}
};
auto menu = Menu(&state->items, &state->selected, option);
return Renderer(menu, [this, menu, state] {
return vbox({
text("🛠️ Tools") | bold | center,
RenderPanelHeader(PanelType::kTools),
separator(),
menu->Render(),
separator(),
text("Select a tool category") | dim | center
}) | border;
text("Select a tool category") | dim | center,
separator(),
RenderCommandHints(),
separator(),
RenderWorkflowLane()
}) | borderRounded | bgcolor(Color::Black);
});
}
@@ -337,47 +450,77 @@ Component UnifiedLayout::CreateHexViewerPanel() {
rows.push_back(hbox(row));
}
auto workflow = RenderWorkflowLane();
return vbox({
text("🔍 Hex Viewer") | bold | center,
separator(),
workflow,
separator(),
vbox(rows) | frame | flex,
separator(),
text(absl::StrFormat("Offset: 0x%08X", *offset)) | color(Color::Cyan),
separator(),
text("↑/↓: Scroll | q: Back") | dim | center
}) | border;
}) | borderRounded | bgcolor(Color::Black);
});
}
Component UnifiedLayout::CreatePaletteEditorPanel() {
return Renderer([this] {
return vbox({
text("🎨 Palette Editor") | bold | center,
RenderPanelHeader(PanelType::kPaletteEditor),
separator(),
text("Palette editing functionality") | center,
text("coming soon...") | center | dim,
RenderResponsiveGrid({
vbox({
text("🌈 Overview") | bold,
text("Preview palette indices and colors"),
text("Highlight sprite-specific palettes") | dim
}) | borderRounded | bgcolor(Color::Black),
vbox({
text("🧪 Roadmap") | bold,
text("• Live recolor with undo stack"),
text("• Sprite preview viewport"),
text("• Export to .pal/.act")
}) | borderRounded | bgcolor(Color::Black),
vbox({
text("🗒 TODO") | bold,
text("Link to command palette"),
text("Use animation timeline"),
text("Add palette history panel") | dim
}) | borderRounded | bgcolor(Color::Black)
}),
separator(),
text("This panel will allow editing") | center,
text("color palettes from the ROM") | center,
RenderWorkflowLane(),
separator(),
text("Press 'q' to go back") | dim | center
}) | border;
}) | borderRounded;
});
}
Component UnifiedLayout::CreateTodoManagerPanel() {
return Renderer([this] {
std::vector<Element> todo_cards;
if (todo_provider_) {
for (const auto& item : todo_provider_()) {
todo_cards.push_back(text("" + item));
}
}
if (todo_cards.empty()) {
todo_cards.push_back(text("No TODOs yet") | dim);
}
return vbox({
text("📝 TODO Manager") | bold | center,
RenderPanelHeader(PanelType::kTodoManager),
separator(),
text("TODO management functionality") | center,
text("coming soon...") | center | dim,
vbox(todo_cards) | borderRounded | bgcolor(Color::Black),
separator(),
text("This panel will integrate with") | center,
text("the existing TODO manager") | center,
text("Press Ctrl+T anywhere to toggle the popup todo overlay.") | dim,
separator(),
RenderWorkflowLane(),
separator(),
text("Press 'q' to go back") | dim | center
}) | border;
}) | borderRounded;
});
}
@@ -385,11 +528,11 @@ Component UnifiedLayout::CreateRomToolsPanel() {
struct ToolsState {
int selected = 0;
std::vector<std::string> items = {
"Apply Asar Patch",
"Apply BPS Patch",
"Extract Symbols",
"Validate Assembly",
"Generate Save File",
"Apply Asar Patch — todo#123",
"Apply BPS Patch — todo#124",
"Extract Symbols — todo#098",
"Validate Assembly — todo#087",
"Generate Save File — todo#142",
"Back"
};
};
@@ -412,8 +555,8 @@ Component UnifiedLayout::CreateGraphicsToolsPanel() {
struct ToolsState {
int selected = 0;
std::vector<std::string> items = {
"Palette Editor",
"Hex Viewer",
"Palette Editor — ref gfx::export",
"Hex Viewer — ref rom::hex",
"Back"
};
};
@@ -478,7 +621,7 @@ Component UnifiedLayout::CreateSettingsPanel() {
return Renderer(controls, [this, controls, state, left_width_control, right_width_control,
bottom_height_control, apply_button, reset_button] {
return vbox({
text("⚙️ Layout Configuration") | bold | center | color(Color::Cyan),
RenderPanelHeader(PanelType::kSettings) | color(Color::Cyan),
separator(),
text("Customize the TUI layout") | center | dim,
separator(),
@@ -530,7 +673,7 @@ Component UnifiedLayout::CreateSettingsPanel() {
Component UnifiedLayout::CreateHelpPanel() {
return Renderer([this] {
return vbox({
text("❓ Z3ED Help") | bold | center | color(Color::Cyan),
RenderPanelHeader(PanelType::kHelp) | color(Color::Cyan),
separator(),
text("Unified TUI Layout - ROM Editor & AI Agent") | center | dim,
separator(),
@@ -620,25 +763,39 @@ Component UnifiedLayout::CreateUnifiedLayout() {
}
// Dynamically select right panel
Component right_panel = config_.show_status ? status_panel_ : tools_panel_;
Component right_panel;
if (config_.show_status) {
right_panel = status_panel_;
} else {
right_panel = tools_panel_;
}
// Create horizontal layout
auto top_section = hbox({
left_panel->Render() | flex,
separator(),
right_panel->Render() | size(WIDTH, EQUAL, config_.right_panel_width)
separatorLight(),
right_panel->Render() | size(WIDTH, LESS_THAN, config_.right_panel_width)
});
// Add chat panel if enabled
if (config_.show_chat) {
return vbox({
top_section | flex,
separator(),
chat_panel_->Render() | size(HEIGHT, EQUAL, config_.bottom_panel_height)
});
Element stacked = vbox({
top_section | flex,
separatorLight(),
chat_panel_->Render() | size(HEIGHT, EQUAL, config_.bottom_panel_height)
}) | bgcolor(Color::Black);
if (todo_overlay_visible_ && todo_overlay_component_) {
stacked = dbox({stacked, todo_overlay_component_->Render()});
}
return stacked;
}
return top_section;
Element content = top_section | bgcolor(Color::Black);
if (todo_overlay_visible_ && todo_overlay_component_) {
content = dbox({content, todo_overlay_component_->Render()});
}
return content;
});
}
@@ -653,6 +810,11 @@ bool UnifiedLayout::HandleGlobalEvents(const Event& event) {
}
// Global shortcuts
if (event == Event::Special({20})) { // Ctrl+T
ToggleTodoOverlay();
return true;
}
if (event == Event::Character('q') ||
(event == Event::Character('q') && state_.active_main_panel == PanelType::kMainMenu)) {
screen_.Exit();
@@ -751,9 +913,37 @@ Element UnifiedLayout::RenderStatusBar() {
text(absl::StrFormat("ROM: %s",
state_.current_rom_file.empty() ? "None" : state_.current_rom_file)) | color(Color::Green),
filler(),
text("q: Quit | h: Help | c: Chat | s: Status") | dim
text("Shortcuts: Ctrl+T TODO Overlay | f Full Chat | m Main Menu") | dim
});
}
Element UnifiedLayout::RenderAnimatedBanner() {
return text("🎮 Z3ED CLI") | bold | center;
}
Element UnifiedLayout::RenderWorkflowLane() {
return text("Workflow: Active") | color(Color::Green);
}
Element UnifiedLayout::RenderCommandHints() {
return vbox({
text("Command Hints:") | bold,
text(" Ctrl+T - Toggle TODO overlay"),
text(" f - Full chat mode"),
text(" m - Main menu")
});
}
Element UnifiedLayout::RenderTodoStack() {
return text("TODO Stack: Empty") | dim;
}
Element UnifiedLayout::RenderResponsiveGrid(const std::vector<Element>& tiles) {
if (tiles.empty()) {
return text("No items") | center | dim;
}
return vbox(tiles);
}
} // namespace cli
} // namespace yaze

View File

@@ -4,6 +4,7 @@
#include <ftxui/component/component.hpp>
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/screen.hpp>
#include <functional>
#include <memory>
#include <string>
#include <vector>
@@ -56,6 +57,10 @@ struct PanelState {
bool status_expanded = false;
std::string current_rom_file;
std::string current_error;
std::string command_palette_hint;
std::string todo_summary;
std::vector<std::string> active_workflows;
double last_tool_latency = 0.0;
};
class UnifiedLayout {
@@ -71,12 +76,17 @@ class UnifiedLayout {
void SwitchToolPanel(PanelType panel);
void ToggleChat();
void ToggleStatus();
void ToggleTodoOverlay();
// Configuration
void SetLayoutConfig(const LayoutConfig& config);
LayoutConfig GetLayoutConfig() const { return config_; }
void SetStatusProvider(std::function<ftxui::Element()> provider);
void SetCommandSummaryProvider(std::function<std::vector<std::string>()> provider);
void SetTodoProvider(std::function<std::vector<std::string>()> provider);
private:
void InitializeTheme();
// Component creation
ftxui::Component CreateMainMenuPanel();
ftxui::Component CreateChatPanel();
@@ -100,6 +110,11 @@ class UnifiedLayout {
// Rendering
ftxui::Element RenderPanelHeader(PanelType panel);
ftxui::Element RenderStatusBar();
ftxui::Element RenderAnimatedBanner();
ftxui::Element RenderWorkflowLane();
ftxui::Element RenderCommandHints();
ftxui::Element RenderTodoStack();
ftxui::Element RenderResponsiveGrid(const std::vector<ftxui::Element>& tiles);
// State
ftxui::ScreenInteractive screen_;
@@ -130,6 +145,14 @@ class UnifiedLayout {
// Event handlers
std::function<bool(const ftxui::Event&)> global_event_handler_;
std::function<bool(const ftxui::Event&)> panel_event_handler_;
// External providers
std::function<ftxui::Element()> status_provider_;
std::function<std::vector<std::string>()> command_summary_provider_;
std::function<std::vector<std::string>()> todo_provider_;
bool todo_overlay_visible_ = false;
ftxui::Component todo_overlay_component_;
};
} // namespace cli

View File

@@ -1,211 +1,43 @@
include(FetchContent)
# This file defines the z3ed command-line tool.
FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
GIT_TAG v5.0.0
)
FetchContent_GetProperties(ftxui)
if(NOT ftxui_POPULATED)
FetchContent_Populate(ftxui)
add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
find_package(yaml-cpp CONFIG)
if(NOT yaml-cpp_FOUND)
message(STATUS "yaml-cpp not found via package config, fetching from source")
FetchContent_Declare(yaml-cpp
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
GIT_TAG 0.8.0
)
FetchContent_GetProperties(yaml-cpp)
if(NOT yaml-cpp_POPULATED)
FetchContent_Populate(yaml-cpp)
set(_yaml_cpp_cmakelists "${yaml-cpp_SOURCE_DIR}/CMakeLists.txt")
if(EXISTS "${_yaml_cpp_cmakelists}")
file(READ "${_yaml_cpp_cmakelists}" _yaml_cpp_cmake_contents)
if(_yaml_cpp_cmake_contents MATCHES "cmake_minimum_required\\(VERSION 3\\.4\\)")
string(REPLACE "cmake_minimum_required(VERSION 3.4)"
"cmake_minimum_required(VERSION 3.5)"
_yaml_cpp_cmake_contents "${_yaml_cpp_cmake_contents}")
file(WRITE "${_yaml_cpp_cmakelists}" "${_yaml_cpp_cmake_contents}")
endif()
endif()
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Disable yaml-cpp tests" FORCE)
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Disable yaml-cpp contrib" FORCE)
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Disable yaml-cpp tools" FORCE)
set(YAML_CPP_INSTALL OFF CACHE BOOL "Disable yaml-cpp install" FORCE)
set(YAML_CPP_FORMAT_SOURCE OFF CACHE BOOL "Disable yaml-cpp format target" FORCE)
add_subdirectory(${yaml-cpp_SOURCE_DIR} ${yaml-cpp_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
endif()
add_executable(
z3ed
add_executable(z3ed
cli/cli_main.cc
cli/cli.cc
cli/tui/tui.cc
cli/tui/unified_layout.cc
cli/tui/enhanced_chat_component.cc
cli/tui/enhanced_status_panel.cc
cli/tui/hex_viewer.cc
# Removed old-style handlers: compress.cc, patch.cc, tile16_transfer.cc
cli/handlers/game/dungeon.cc
cli/handlers/graphics/gfx.cc
cli/handlers/graphics/palette.cc
cli/handlers/rom/rom_commands.cc
cli/handlers/game/overworld.cc
cli/handlers/game/overworld_inspect.cc
# Removed old-style handlers: sprite.cc, command_palette.cc
cli/handlers/rom/project_commands.cc
cli/handlers/game/message.cc
cli/handlers/agent.cc
cli/handlers/agent/common.cc
cli/handlers/agent/general_commands.cc
cli/handlers/agent/conversation_test.cc
cli/handlers/agent/test_common.cc
cli/handlers/agent/test_commands.cc
cli/handlers/agent/todo_commands.cc
# New CommandHandler-based implementations
cli/handlers/tools/resource_commands.cc
cli/handlers/game/dungeon_commands.cc
cli/handlers/game/overworld_commands.cc
cli/handlers/tools/gui_commands.cc
cli/handlers/graphics/hex_commands.cc
cli/handlers/game/dialogue_commands.cc
cli/handlers/game/music_commands.cc
cli/handlers/graphics/palette_commands.cc
# cli/handlers/graphics/sprite_commands.cc # File doesn't exist
cli/handlers/tools/emulator_commands.cc
cli/handlers/game/message_commands.cc
cli/handlers/command_wrappers.cc
cli/flags.cc
cli/tui/asar_patch.cc
cli/tui/palette_editor.cc
cli/tui/command_palette.cc
cli/tui/chat_tui.cc
cli/tui/autocomplete_ui.cc
cli/util/autocomplete.cc
cli/service/agent/vim_mode.cc
cli/service/testing/test_suite_loader.cc
cli/service/testing/test_suite_reporter.cc
cli/service/testing/test_suite_writer.cc
# ... (source files)
)
target_compile_definitions(z3ed PRIVATE YAZE_ASSETS_PATH="${CMAKE_SOURCE_DIR}/assets")
# ============================================================================
# Copy Agent Assets for z3ed CLI
# ============================================================================
# Copy agent assets to build directory so z3ed can find them when running
# Copy agent assets for z3ed
if(EXISTS ${CMAKE_SOURCE_DIR}/assets/agent)
file(GLOB AGENT_ASSET_FILES "${CMAKE_SOURCE_DIR}/assets/agent/*")
file(COPY ${AGENT_ASSET_FILES} DESTINATION "${CMAKE_BINARY_DIR}/assets/agent/")
# Also add post-build copy for development workflow
file(COPY ${CMAKE_SOURCE_DIR}/assets/agent/ DESTINATION "${CMAKE_BINARY_DIR}/assets/agent/")
add_custom_command(TARGET z3ed POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/assets/agent
$<TARGET_FILE_DIR:z3ed>/assets/agent
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/agent $<TARGET_FILE_DIR:z3ed>/assets/agent
COMMENT "Copying agent assets for z3ed"
)
endif()
# ============================================================================
# AI Agent Support (Consolidated via Z3ED_AI flag)
# ============================================================================
if(Z3ED_AI OR YAZE_WITH_JSON)
target_compile_definitions(z3ed PRIVATE YAZE_WITH_JSON)
message(STATUS "✓ z3ed AI agent enabled (Ollama + Gemini support)")
# Link nlohmann_json (already fetched in main CMakeLists if YAZE_WITH_JSON)
target_link_libraries(z3ed PRIVATE nlohmann_json::nlohmann_json)
endif()
# ============================================================================
# SSL/HTTPS Support (Optional - Required for Gemini API)
# ============================================================================
# SSL is only enabled when AI features are active
# Ollama (localhost) works without SSL, Gemini (HTTPS) requires it
if((Z3ED_AI OR YAZE_WITH_JSON) AND (YAZE_WITH_GRPC OR Z3ED_AI))
find_package(OpenSSL)
if(OpenSSL_FOUND)
# Define the SSL support macro for httplib
target_compile_definitions(z3ed PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
# Link OpenSSL libraries
target_link_libraries(z3ed PRIVATE OpenSSL::SSL OpenSSL::Crypto)
# On macOS, also enable Keychain cert support
if(APPLE)
target_compile_definitions(z3ed PRIVATE CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
target_link_libraries(z3ed PRIVATE "-framework CoreFoundation" "-framework Security")
endif()
message(STATUS "✓ SSL/HTTPS support enabled for z3ed (Gemini API ready)")
else()
message(WARNING "OpenSSL not found - Gemini API will not work")
message(STATUS " • Ollama (local) still works without SSL")
message(STATUS " • Install OpenSSL for Gemini: brew install openssl (macOS) or apt install libssl-dev (Linux)")
endif()
else()
if(NOT Z3ED_AI AND NOT YAZE_WITH_JSON)
message(STATUS "○ z3ed AI agent disabled (set -DZ3ED_AI=ON to enable Gemini/Ollama)")
endif()
endif()
target_include_directories(z3ed PUBLIC
target_include_directories(z3ed PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/tui"
)
# (Link libraries handled below; duplicate/unfinished lines removed.)
if(YAZE_USE_MODULAR_BUILD)
target_link_libraries(
z3ed PRIVATE
target_link_libraries(z3ed PRIVATE
yaze_core
yaze_agent
ftxui::component
)
else()
target_link_libraries(
z3ed PRIVATE
yaze_core
yaze_agent
ftxui::component
absl::flags
absl::flags_parse
)
)
if(Z3ED_AI)
target_link_libraries(z3ed PRIVATE yaml-cpp)
endif()
# ============================================================================
# Optional gRPC Support for CLI Agent Test Command
# ============================================================================
if(YAZE_WITH_GRPC)
message(STATUS "Adding gRPC support to z3ed CLI")
# Generate C++ code from .proto using the helper function from cmake/grpc.cmake
target_add_protobuf(z3ed
${CMAKE_SOURCE_DIR}/src/protos/imgui_test_harness.proto
${CMAKE_SOURCE_DIR}/src/protos/canvas_automation.proto)
# Add CLI gRPC service sources
target_sources(z3ed PRIVATE
${CMAKE_SOURCE_DIR}/src/cli/service/gui/gui_automation_client.cc
${CMAKE_SOURCE_DIR}/src/cli/service/gui/gui_automation_client.h
${CMAKE_SOURCE_DIR}/src/cli/service/testing/test_workflow_generator.cc
${CMAKE_SOURCE_DIR}/src/cli/service/testing/test_workflow_generator.h)
# Link gRPC libraries
target_link_libraries(z3ed PRIVATE
grpc++
grpc++_reflection
libprotobuf)
message(STATUS "✓ gRPC CLI automation integrated")
target_link_libraries(z3ed PRIVATE grpc++ grpc++_reflection libprotobuf)
endif()

View File

@@ -59,6 +59,7 @@ inline std::string GetColoredLogo() {
" ▲ ▲ " + "\033[0;37m" + "AI-Powered CLI\n" + // Gray
"\033[1;33m" +
" ▲▲▲▲▲ \n" +
"\033[1;32m" + " FTXUI ✦ Animations ✦ Command TODOs" + "\n" +
"\033[0m"; // Reset
}