Refactor Agent Commands and Enhance Resource Context Management
- Updated the immediate action plan to focus on integrating `Tile16ProposalGenerator` and `ResourceContextBuilder` into agent commands, improving command handling and proposal generation. - Implemented the `SetTile` method in the `Overworld` class to facilitate tile modifications based on the current world context. - Enhanced error handling in command execution to ensure robust feedback during ROM operations. - Created new files for `Tile16ProposalGenerator` and `ResourceContextBuilder`, enabling structured management of tile changes and resource labels for AI prompts. This commit advances the functionality of the z3ed system, laying the groundwork for more sophisticated AI-driven editing capabilities.
This commit is contained in:
@@ -253,33 +253,31 @@ unset GEMINI_API_KEY
|
|||||||
|
|
||||||
## 🚀 Next Steps
|
## 🚀 Next Steps
|
||||||
|
|
||||||
### Immediate Actions (Today)
|
### Immediate Actions (Next Session)
|
||||||
|
|
||||||
1. **Test Ollama Integration** (30 min)
|
1. **Integrate Tile16ProposalGenerator into Agent Commands** (2 hours)
|
||||||
|
- Modify `HandlePlanCommand()` to use generator
|
||||||
|
- Modify `HandleRunCommand()` to apply proposals
|
||||||
|
- Add `HandleAcceptCommand()` for accepting proposals
|
||||||
|
|
||||||
|
2. **Integrate ResourceContextBuilder into PromptBuilder** (1 hour)
|
||||||
|
- Update `BuildContextualPrompt()` to inject labels
|
||||||
|
- Test with actual labels file from user project
|
||||||
|
|
||||||
|
3. **Test End-to-End Workflow** (1 hour)
|
||||||
```bash
|
```bash
|
||||||
ollama serve
|
ollama serve
|
||||||
ollama pull qwen2.5-coder:7b
|
./build-grpc-test/bin/z3ed agent plan \
|
||||||
./build-grpc-test/bin/z3ed agent plan --prompt "test"
|
--prompt "Create a 3x3 water pond at 15, 10"
|
||||||
|
|
||||||
|
# Verify proposal generation
|
||||||
|
# Verify tile16 changes are correct
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Test Gemini Integration** (30 min)
|
4. **Add Visual Diff Implementation** (2-3 hours)
|
||||||
```bash
|
- Render tile16 bitmaps from overworld
|
||||||
export GEMINI_API_KEY="your-key"
|
- Create side-by-side comparison images
|
||||||
./build-grpc-test/bin/z3ed agent plan --prompt "test"
|
- Highlight changed tiles
|
||||||
```
|
|
||||||
|
|
||||||
3. **Run End-to-End Test** (1 hour)
|
|
||||||
```bash
|
|
||||||
./build-grpc-test/bin/z3ed agent run \
|
|
||||||
--prompt "Change palette 0 color 5 to red" \
|
|
||||||
--rom assets/zelda3.sfc \
|
|
||||||
--sandbox
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Document Results** (30 min)
|
|
||||||
- Create `TESTING-RESULTS.md` with actual outputs
|
|
||||||
- Update `GEMINI-TESTING-STATUS.md` with validation
|
|
||||||
- Mark Phase 2 & 4 as validated in checklists
|
|
||||||
|
|
||||||
### Short-Term (This Week)
|
### Short-Term (This Week)
|
||||||
|
|
||||||
@@ -351,13 +349,17 @@ unset GEMINI_API_KEY
|
|||||||
|
|
||||||
## 📝 Files Summary
|
## 📝 Files Summary
|
||||||
|
|
||||||
### Created/Modified in This Session
|
### Created/Modified Recently
|
||||||
- ✅ `src/cli/handlers/agent/test_common.{h,cc}` (NEW)
|
- ✅ `src/cli/handlers/agent/test_common.{h,cc}` (NEW)
|
||||||
- ✅ `src/cli/handlers/agent/test_commands.cc` (REBUILT)
|
- ✅ `src/cli/handlers/agent/test_commands.cc` (REBUILT)
|
||||||
- ✅ `src/cli/z3ed.cmake` (UPDATED)
|
- ✅ `src/cli/z3ed.cmake` (UPDATED)
|
||||||
- ✅ `src/cli/service/gemini_ai_service.cc` (FIXED includes)
|
- ✅ `src/cli/service/gemini_ai_service.cc` (FIXED includes)
|
||||||
- ✅ `docs/z3ed/BUILD-FIX-COMPLETED.md` (NEW)
|
- ✅ `src/cli/service/tile16_proposal_generator.{h,cc}` (NEW - Oct 3) ✨
|
||||||
- ✅ `docs/z3ed/AGENTIC-PLAN-STATUS.md` (NEW - this file)
|
- ✅ `src/cli/service/resource_context_builder.{h,cc}` (NEW - Oct 3) ✨
|
||||||
|
- ✅ `src/app/zelda3/overworld/overworld.h` (UPDATED - SetTile method) ✨
|
||||||
|
- ✅ `src/cli/handlers/overworld.cc` (UPDATED - SetTile implementation) ✨
|
||||||
|
- ✅ `docs/z3ed/IMPLEMENTATION-SESSION-OCT3-CONTINUED.md` (NEW) ✨
|
||||||
|
- ✅ `docs/z3ed/AGENTIC-PLAN-STATUS.md` (UPDATED - this file)
|
||||||
|
|
||||||
### Previously Implemented (Phase 1-4)
|
### Previously Implemented (Phase 1-4)
|
||||||
- ✅ `src/cli/service/ollama_ai_service.{h,cc}`
|
- ✅ `src/cli/service/ollama_ai_service.{h,cc}`
|
||||||
|
|||||||
@@ -287,6 +287,15 @@ class Overworld {
|
|||||||
return map_tiles_.special_world[y][x];
|
return map_tiles_.special_world[y][x];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void SetTile(int x, int y, uint16_t tile_id) {
|
||||||
|
if (current_world_ == 0) {
|
||||||
|
map_tiles_.light_world[y][x] = tile_id;
|
||||||
|
} else if (current_world_ == 1) {
|
||||||
|
map_tiles_.dark_world[y][x] = tile_id;
|
||||||
|
} else {
|
||||||
|
map_tiles_.special_world[y][x] = tile_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
auto map_tiles() const { return map_tiles_; }
|
auto map_tiles() const { return map_tiles_; }
|
||||||
auto mutable_map_tiles() { return &map_tiles_; }
|
auto mutable_map_tiles() { return &map_tiles_; }
|
||||||
auto all_items() const { return all_items_; }
|
auto all_items() const { return all_items_; }
|
||||||
|
|||||||
@@ -23,13 +23,19 @@ absl::Status OverworldGetTile::Run(const std::vector<std::string>& arg_vec) {
|
|||||||
return absl::InvalidArgumentError("ROM file must be provided via --rom flag.");
|
return absl::InvalidArgumentError("ROM file must be provided via --rom flag.");
|
||||||
}
|
}
|
||||||
|
|
||||||
rom_.LoadFromFile(rom_file);
|
auto load_status = rom_.LoadFromFile(rom_file);
|
||||||
|
if (!load_status.ok()) {
|
||||||
|
return load_status;
|
||||||
|
}
|
||||||
if (!rom_.is_loaded()) {
|
if (!rom_.is_loaded()) {
|
||||||
return absl::AbortedError("Failed to load ROM.");
|
return absl::AbortedError("Failed to load ROM.");
|
||||||
}
|
}
|
||||||
|
|
||||||
zelda3::Overworld overworld(&rom_);
|
zelda3::Overworld overworld(&rom_);
|
||||||
overworld.Load(&rom_);
|
auto ow_status = overworld.Load(&rom_);
|
||||||
|
if (!ow_status.ok()) {
|
||||||
|
return ow_status;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t tile = overworld.GetTile(x, y);
|
uint16_t tile = overworld.GetTile(x, y);
|
||||||
|
|
||||||
@@ -54,21 +60,40 @@ absl::Status OverworldSetTile::Run(const std::vector<std::string>& arg_vec) {
|
|||||||
return absl::InvalidArgumentError("ROM file must be provided via --rom flag.");
|
return absl::InvalidArgumentError("ROM file must be provided via --rom flag.");
|
||||||
}
|
}
|
||||||
|
|
||||||
rom_.LoadFromFile(rom_file);
|
auto load_status = rom_.LoadFromFile(rom_file);
|
||||||
|
if (!load_status.ok()) {
|
||||||
|
return load_status;
|
||||||
|
}
|
||||||
if (!rom_.is_loaded()) {
|
if (!rom_.is_loaded()) {
|
||||||
return absl::AbortedError("Failed to load ROM.");
|
return absl::AbortedError("Failed to load ROM.");
|
||||||
}
|
}
|
||||||
|
|
||||||
zelda3::Overworld overworld(&rom_);
|
zelda3::Overworld overworld(&rom_);
|
||||||
overworld.Load(&rom_);
|
auto status = overworld.Load(&rom_);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Implement the actual set_tile method in Overworld class
|
// Set the world based on map_id
|
||||||
// overworld.SetTile(x, y, tile_id);
|
if (map_id < 0x40) {
|
||||||
|
overworld.set_current_world(0); // Light World
|
||||||
|
} else if (map_id < 0x80) {
|
||||||
|
overworld.set_current_world(1); // Dark World
|
||||||
|
} else {
|
||||||
|
overworld.set_current_world(2); // Special World
|
||||||
|
}
|
||||||
|
|
||||||
// rom_.SaveToFile({.filename = rom_file});
|
// Set the tile
|
||||||
|
overworld.SetTile(x, y, static_cast<uint16_t>(tile_id));
|
||||||
|
|
||||||
std::cout << "Set tile at (" << x << ", " << y << ") on map " << map_id << " to: 0x" << std::hex << tile_id << std::endl;
|
// Save the ROM
|
||||||
std::cout << "(Not actually implemented yet)" << std::endl;
|
auto save_status = rom_.SaveToFile({.filename = rom_file});
|
||||||
|
if (!save_status.ok()) {
|
||||||
|
return save_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "✅ Set tile at (" << x << ", " << y << ") on map " << map_id
|
||||||
|
<< " to: 0x" << std::hex << tile_id << std::dec << std::endl;
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
262
src/cli/service/resource_context_builder.cc
Normal file
262
src/cli/service/resource_context_builder.cc
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
#include "cli/service/resource_context_builder.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace cli {
|
||||||
|
|
||||||
|
std::string ResourceContextBuilder::GetCommonTile16Reference() {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Common Tile16s:\n";
|
||||||
|
oss << " - 0x020: Grass\n";
|
||||||
|
oss << " - 0x022: Dirt\n";
|
||||||
|
oss << " - 0x02E: Tree\n";
|
||||||
|
oss << " - 0x003: Bush\n";
|
||||||
|
oss << " - 0x004: Rock\n";
|
||||||
|
oss << " - 0x021: Flower\n";
|
||||||
|
oss << " - 0x023: Sand\n";
|
||||||
|
oss << " - 0x14C: Water (top edge)\n";
|
||||||
|
oss << " - 0x14D: Water (middle)\n";
|
||||||
|
oss << " - 0x14E: Water (bottom edge)\n";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ResourceContextBuilder::ExtractOverworldLabels() {
|
||||||
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
return "Overworld Maps: (ROM not loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* label_mgr = rom_->resource_label();
|
||||||
|
if (!label_mgr || !label_mgr->labels_loaded_) {
|
||||||
|
return "Overworld Maps: (No labels file loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Overworld Maps:\n";
|
||||||
|
|
||||||
|
// Check if "overworld" labels exist
|
||||||
|
auto it = label_mgr->labels_.find("overworld");
|
||||||
|
if (it != label_mgr->labels_.end()) {
|
||||||
|
for (const auto& [key, value] : it->second) {
|
||||||
|
oss << " - " << key << ": \"" << value << "\"\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Provide defaults
|
||||||
|
oss << " - 0: \"Light World\"\n";
|
||||||
|
oss << " - 1: \"Dark World\"\n";
|
||||||
|
oss << " - 3: \"Desert\"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ResourceContextBuilder::ExtractDungeonLabels() {
|
||||||
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
return "Dungeons: (ROM not loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* label_mgr = rom_->resource_label();
|
||||||
|
if (!label_mgr || !label_mgr->labels_loaded_) {
|
||||||
|
return "Dungeons: (No labels file loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Dungeons:\n";
|
||||||
|
|
||||||
|
// Check if "dungeon" labels exist
|
||||||
|
auto it = label_mgr->labels_.find("dungeon");
|
||||||
|
if (it != label_mgr->labels_.end()) {
|
||||||
|
for (const auto& [key, value] : it->second) {
|
||||||
|
oss << " - " << key << ": \"" << value << "\"\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Provide vanilla defaults
|
||||||
|
oss << " - 0x00: \"Hyrule Castle\"\n";
|
||||||
|
oss << " - 0x02: \"Eastern Palace\"\n";
|
||||||
|
oss << " - 0x04: \"Desert Palace\"\n";
|
||||||
|
oss << " - 0x06: \"Tower of Hera\"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ResourceContextBuilder::ExtractEntranceLabels() {
|
||||||
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
return "Entrances: (ROM not loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* label_mgr = rom_->resource_label();
|
||||||
|
if (!label_mgr || !label_mgr->labels_loaded_) {
|
||||||
|
return "Entrances: (No labels file loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Entrances:\n";
|
||||||
|
|
||||||
|
// Check if "entrance" labels exist
|
||||||
|
auto it = label_mgr->labels_.find("entrance");
|
||||||
|
if (it != label_mgr->labels_.end()) {
|
||||||
|
for (const auto& [key, value] : it->second) {
|
||||||
|
oss << " - " << key << ": \"" << value << "\"\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Provide vanilla defaults
|
||||||
|
oss << " - 0x00: \"Link's House\"\n";
|
||||||
|
oss << " - 0x01: \"Sanctuary\"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ResourceContextBuilder::ExtractRoomLabels() {
|
||||||
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
return "Rooms: (ROM not loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* label_mgr = rom_->resource_label();
|
||||||
|
if (!label_mgr || !label_mgr->labels_loaded_) {
|
||||||
|
return "Rooms: (No labels file loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Rooms:\n";
|
||||||
|
|
||||||
|
// Check if "room" labels exist
|
||||||
|
auto it = label_mgr->labels_.find("room");
|
||||||
|
if (it != label_mgr->labels_.end()) {
|
||||||
|
for (const auto& [key, value] : it->second) {
|
||||||
|
oss << " - " << key << ": \"" << value << "\"\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oss << " (No room labels defined)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ResourceContextBuilder::ExtractSpriteLabels() {
|
||||||
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
return "Sprites: (ROM not loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* label_mgr = rom_->resource_label();
|
||||||
|
if (!label_mgr || !label_mgr->labels_loaded_) {
|
||||||
|
return "Sprites: (No labels file loaded)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Sprites:\n";
|
||||||
|
|
||||||
|
// Check if "sprite" labels exist
|
||||||
|
auto it = label_mgr->labels_.find("sprite");
|
||||||
|
if (it != label_mgr->labels_.end()) {
|
||||||
|
for (const auto& [key, value] : it->second) {
|
||||||
|
oss << " - " << key << ": \"" << value << "\"\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Provide vanilla defaults
|
||||||
|
oss << " - 0x00: \"Soldier\"\n";
|
||||||
|
oss << " - 0x01: \"Octorok\"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::StatusOr<std::string> ResourceContextBuilder::BuildResourceContext() {
|
||||||
|
if (!rom_) {
|
||||||
|
return absl::InvalidArgumentError("ROM pointer is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream context;
|
||||||
|
|
||||||
|
context << "=== AVAILABLE RESOURCES ===\n\n";
|
||||||
|
|
||||||
|
// Add overworld maps
|
||||||
|
context << ExtractOverworldLabels() << "\n";
|
||||||
|
|
||||||
|
// Add dungeons
|
||||||
|
context << ExtractDungeonLabels() << "\n";
|
||||||
|
|
||||||
|
// Add entrances
|
||||||
|
context << ExtractEntranceLabels() << "\n";
|
||||||
|
|
||||||
|
// Add rooms (if any)
|
||||||
|
context << ExtractRoomLabels() << "\n";
|
||||||
|
|
||||||
|
// Add sprites
|
||||||
|
context << ExtractSpriteLabels() << "\n";
|
||||||
|
|
||||||
|
// Add common tile16 reference
|
||||||
|
context << GetCommonTile16Reference() << "\n";
|
||||||
|
|
||||||
|
context << "=== INSTRUCTIONS ===\n";
|
||||||
|
context << "1. Use the resource labels when they're available\n";
|
||||||
|
context << "2. If a user refers to a custom name, check the labels above\n";
|
||||||
|
context << "3. Always provide tile16 IDs as hex values (0x###)\n";
|
||||||
|
context << "4. Explain which resources you're using in your reasoning\n";
|
||||||
|
|
||||||
|
return context.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::StatusOr<std::map<std::string, std::string>>
|
||||||
|
ResourceContextBuilder::GetLabels(const std::string& category) {
|
||||||
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
return absl::FailedPreconditionError("ROM not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* label_mgr = rom_->resource_label();
|
||||||
|
if (!label_mgr || !label_mgr->labels_loaded_) {
|
||||||
|
return absl::FailedPreconditionError("No labels file loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> result;
|
||||||
|
|
||||||
|
auto it = label_mgr->labels_.find(category);
|
||||||
|
if (it != label_mgr->labels_.end()) {
|
||||||
|
for (const auto& [key, value] : it->second) {
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::StatusOr<std::string> ResourceContextBuilder::ExportToJson() {
|
||||||
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
return absl::InvalidArgumentError("ROM not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* label_mgr = rom_->resource_label();
|
||||||
|
if (!label_mgr || !label_mgr->labels_loaded_) {
|
||||||
|
return absl::InvalidArgumentError("No labels file loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream json;
|
||||||
|
json << "{\n";
|
||||||
|
|
||||||
|
bool first_category = true;
|
||||||
|
for (const auto& [category, labels] : label_mgr->labels_) {
|
||||||
|
if (!first_category) json << ",\n";
|
||||||
|
first_category = false;
|
||||||
|
|
||||||
|
json << " \"" << category << "\": {\n";
|
||||||
|
|
||||||
|
bool first_label = true;
|
||||||
|
for (const auto& [key, value] : labels) {
|
||||||
|
if (!first_label) json << ",\n";
|
||||||
|
first_label = false;
|
||||||
|
|
||||||
|
json << " \"" << key << "\": \"" << value << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
json << "\n }";
|
||||||
|
}
|
||||||
|
|
||||||
|
json << "\n}\n";
|
||||||
|
|
||||||
|
return json.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cli
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
163
src/cli/service/resource_context_builder.h
Normal file
163
src/cli/service/resource_context_builder.h
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#ifndef YAZE_CLI_SERVICE_RESOURCE_CONTEXT_BUILDER_H_
|
||||||
|
#define YAZE_CLI_SERVICE_RESOURCE_CONTEXT_BUILDER_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "absl/status/statusor.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace cli {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Builds contextual information from ROM resources for AI prompts.
|
||||||
|
*
|
||||||
|
* This class extracts user-defined labels from the ROM's ResourceLabelManager
|
||||||
|
* and formats them into human-readable context that can be injected into
|
||||||
|
* AI prompts. This enables AI to use meaningful names like "eastern_palace"
|
||||||
|
* instead of opaque IDs like "0x02".
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* ResourceContextBuilder builder(rom);
|
||||||
|
* std::string context = builder.BuildResourceContext().value();
|
||||||
|
* // Context contains formatted labels for all resource types
|
||||||
|
*/
|
||||||
|
class ResourceContextBuilder {
|
||||||
|
public:
|
||||||
|
explicit ResourceContextBuilder(Rom* rom) : rom_(rom) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build a complete resource context string for AI prompts.
|
||||||
|
*
|
||||||
|
* Extracts all ResourceLabels from the ROM and formats them into
|
||||||
|
* a structured text format suitable for AI consumption.
|
||||||
|
*
|
||||||
|
* Example output:
|
||||||
|
* ```
|
||||||
|
* === AVAILABLE RESOURCES ===
|
||||||
|
*
|
||||||
|
* Overworld Maps:
|
||||||
|
* - 0: "Light World" (user: "hyrule_overworld")
|
||||||
|
* - 1: "Dark World" (user: "dark_world")
|
||||||
|
*
|
||||||
|
* Dungeons:
|
||||||
|
* - 0x00: "Hyrule Castle" (user: "castle")
|
||||||
|
* - 0x02: "Eastern Palace" (user: "east_palace")
|
||||||
|
*
|
||||||
|
* Common Tile16s:
|
||||||
|
* - 0x020: Grass
|
||||||
|
* - 0x02E: Tree
|
||||||
|
* - 0x14C: Water (top)
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @return Formatted resource context string
|
||||||
|
*/
|
||||||
|
absl::StatusOr<std::string> BuildResourceContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get labels for a specific resource category.
|
||||||
|
*
|
||||||
|
* @param category Resource type ("overworld", "dungeon", "entrance", etc.)
|
||||||
|
* @return Map of ID -> label for that category
|
||||||
|
*/
|
||||||
|
absl::StatusOr<std::map<std::string, std::string>> GetLabels(
|
||||||
|
const std::string& category);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Export all labels to JSON format.
|
||||||
|
*
|
||||||
|
* Creates a structured JSON representation of all resources
|
||||||
|
* for potential use by AI services.
|
||||||
|
*
|
||||||
|
* @return JSON string with all resource labels
|
||||||
|
*/
|
||||||
|
absl::StatusOr<std::string> ExportToJson();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Rom* rom_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract overworld map labels.
|
||||||
|
*
|
||||||
|
* Returns formatted string like:
|
||||||
|
* ```
|
||||||
|
* Overworld Maps:
|
||||||
|
* - 0: "Light World" (user: "hyrule_overworld")
|
||||||
|
* - 1: "Dark World" (user: "dark_world")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
std::string ExtractOverworldLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract dungeon labels.
|
||||||
|
*
|
||||||
|
* Returns formatted string like:
|
||||||
|
* ```
|
||||||
|
* Dungeons:
|
||||||
|
* - 0x00: "Hyrule Castle" (user: "castle")
|
||||||
|
* - 0x02: "Eastern Palace" (user: "east_palace")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
std::string ExtractDungeonLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract entrance labels.
|
||||||
|
*
|
||||||
|
* Returns formatted string like:
|
||||||
|
* ```
|
||||||
|
* Entrances:
|
||||||
|
* - 0x00: "Link's House" (user: "starting_house")
|
||||||
|
* - 0x01: "Sanctuary" (user: "church")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
std::string ExtractEntranceLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract room labels.
|
||||||
|
*
|
||||||
|
* Returns formatted string like:
|
||||||
|
* ```
|
||||||
|
* Rooms:
|
||||||
|
* - 0x00_0x10: "Eastern Palace Boss Room"
|
||||||
|
* - 0x04_0x05: "Desert Palace Treasure Room"
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
std::string ExtractRoomLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract sprite labels.
|
||||||
|
*
|
||||||
|
* Returns formatted string like:
|
||||||
|
* ```
|
||||||
|
* Sprites:
|
||||||
|
* - 0x00: "Soldier" (user: "green_soldier")
|
||||||
|
* - 0x01: "Octorok" (user: "red_octorok")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
std::string ExtractSpriteLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add common tile16 reference for AI.
|
||||||
|
*
|
||||||
|
* Provides a quick reference of common tile16 IDs that AI
|
||||||
|
* can use without needing to search through the entire tileset.
|
||||||
|
*
|
||||||
|
* Returns formatted string like:
|
||||||
|
* ```
|
||||||
|
* Common Tile16s:
|
||||||
|
* - 0x020: Grass
|
||||||
|
* - 0x022: Dirt
|
||||||
|
* - 0x02E: Tree
|
||||||
|
* - 0x14C: Water (top edge)
|
||||||
|
* - 0x14D: Water (middle)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
std::string GetCommonTile16Reference();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cli
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_CLI_SERVICE_RESOURCE_CONTEXT_BUILDER_H_
|
||||||
|
|
||||||
263
src/cli/service/tile16_proposal_generator.cc
Normal file
263
src/cli/service/tile16_proposal_generator.cc
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
#include "cli/service/tile16_proposal_generator.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "absl/strings/match.h"
|
||||||
|
#include "absl/strings/str_split.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace cli {
|
||||||
|
|
||||||
|
std::string Tile16Change::ToString() const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Map " << map_id << " @ (" << x << "," << y << "): "
|
||||||
|
<< "0x" << std::hex << old_tile << " → 0x" << new_tile;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Tile16Proposal::ToJson() const {
|
||||||
|
std::ostringstream json;
|
||||||
|
json << "{\n";
|
||||||
|
json << " \"id\": \"" << id << "\",\n";
|
||||||
|
json << " \"prompt\": \"" << prompt << "\",\n";
|
||||||
|
json << " \"ai_service\": \"" << ai_service << "\",\n";
|
||||||
|
json << " \"reasoning\": \"" << reasoning << "\",\n";
|
||||||
|
json << " \"status\": ";
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case Status::PENDING: json << "\"pending\""; break;
|
||||||
|
case Status::ACCEPTED: json << "\"accepted\""; break;
|
||||||
|
case Status::REJECTED: json << "\"rejected\""; break;
|
||||||
|
case Status::APPLIED: json << "\"applied\""; break;
|
||||||
|
}
|
||||||
|
json << ",\n";
|
||||||
|
|
||||||
|
json << " \"changes\": [\n";
|
||||||
|
for (size_t i = 0; i < changes.size(); ++i) {
|
||||||
|
const auto& change = changes[i];
|
||||||
|
json << " {\n";
|
||||||
|
json << " \"map_id\": " << change.map_id << ",\n";
|
||||||
|
json << " \"x\": " << change.x << ",\n";
|
||||||
|
json << " \"y\": " << change.y << ",\n";
|
||||||
|
json << " \"old_tile\": \"0x" << std::hex << change.old_tile << "\",\n";
|
||||||
|
json << " \"new_tile\": \"0x" << std::hex << change.new_tile << "\"\n";
|
||||||
|
json << " }";
|
||||||
|
if (i < changes.size() - 1) json << ",";
|
||||||
|
json << "\n";
|
||||||
|
}
|
||||||
|
json << " ]\n";
|
||||||
|
json << "}\n";
|
||||||
|
|
||||||
|
return json.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::StatusOr<Tile16Proposal> Tile16Proposal::FromJson(const std::string& /* json */) {
|
||||||
|
// TODO: Implement JSON parsing using nlohmann/json when available
|
||||||
|
return absl::UnimplementedError("JSON parsing not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Tile16ProposalGenerator::GenerateProposalId() const {
|
||||||
|
// Generate a simple timestamp-based ID
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
now.time_since_epoch()).count();
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "proposal_" << ms;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::StatusOr<Tile16Change> Tile16ProposalGenerator::ParseSetTileCommand(
|
||||||
|
const std::string& command,
|
||||||
|
Rom* rom) {
|
||||||
|
|
||||||
|
// Expected format: "overworld set-tile --map 0 --x 10 --y 20 --tile 0x02E"
|
||||||
|
std::vector<std::string> parts = absl::StrSplit(command, ' ');
|
||||||
|
|
||||||
|
if (parts.size() < 10) {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
absl::StrCat("Invalid command format: ", command));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[0] != "overworld" || parts[1] != "set-tile") {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
absl::StrCat("Not a set-tile command: ", command));
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile16Change change;
|
||||||
|
|
||||||
|
// Parse arguments
|
||||||
|
for (size_t i = 2; i < parts.size(); i += 2) {
|
||||||
|
if (i + 1 >= parts.size()) break;
|
||||||
|
|
||||||
|
const std::string& flag = parts[i];
|
||||||
|
const std::string& value = parts[i + 1];
|
||||||
|
|
||||||
|
if (flag == "--map") {
|
||||||
|
change.map_id = std::stoi(value);
|
||||||
|
} else if (flag == "--x") {
|
||||||
|
change.x = std::stoi(value);
|
||||||
|
} else if (flag == "--y") {
|
||||||
|
change.y = std::stoi(value);
|
||||||
|
} else if (flag == "--tile") {
|
||||||
|
// Parse as hex (both 0x prefix and plain hex)
|
||||||
|
change.new_tile = static_cast<uint16_t>(std::stoi(value, nullptr, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the ROM to get the old tile value
|
||||||
|
if (rom && rom->is_loaded()) {
|
||||||
|
zelda3::Overworld overworld(rom);
|
||||||
|
auto status = overworld.Load(rom);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the correct world based on map_id
|
||||||
|
if (change.map_id < 0x40) {
|
||||||
|
overworld.set_current_world(0); // Light World
|
||||||
|
} else if (change.map_id < 0x80) {
|
||||||
|
overworld.set_current_world(1); // Dark World
|
||||||
|
} else {
|
||||||
|
overworld.set_current_world(2); // Special World
|
||||||
|
}
|
||||||
|
|
||||||
|
change.old_tile = overworld.GetTile(change.x, change.y);
|
||||||
|
} else {
|
||||||
|
change.old_tile = 0x0000; // Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::StatusOr<Tile16Proposal> Tile16ProposalGenerator::GenerateFromCommands(
|
||||||
|
const std::string& prompt,
|
||||||
|
const std::vector<std::string>& commands,
|
||||||
|
const std::string& ai_service,
|
||||||
|
Rom* rom) {
|
||||||
|
|
||||||
|
Tile16Proposal proposal;
|
||||||
|
proposal.id = GenerateProposalId();
|
||||||
|
proposal.prompt = prompt;
|
||||||
|
proposal.ai_service = ai_service;
|
||||||
|
proposal.created_at = std::chrono::system_clock::now();
|
||||||
|
proposal.status = Tile16Proposal::Status::PENDING;
|
||||||
|
|
||||||
|
// Parse each command
|
||||||
|
for (const auto& command : commands) {
|
||||||
|
// Skip empty commands or comments
|
||||||
|
if (command.empty() || command[0] == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a set-tile command
|
||||||
|
if (absl::StrContains(command, "overworld set-tile")) {
|
||||||
|
auto change_or = ParseSetTileCommand(command, rom);
|
||||||
|
if (change_or.ok()) {
|
||||||
|
proposal.changes.push_back(change_or.value());
|
||||||
|
} else {
|
||||||
|
return change_or.status();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Add support for other command types (set-area, replace-tile, etc.)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proposal.changes.empty()) {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
"No valid tile16 changes found in commands");
|
||||||
|
}
|
||||||
|
|
||||||
|
proposal.reasoning = absl::StrCat(
|
||||||
|
"Generated ", proposal.changes.size(), " tile16 changes from prompt");
|
||||||
|
|
||||||
|
return proposal;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status Tile16ProposalGenerator::ApplyProposal(
|
||||||
|
const Tile16Proposal& proposal,
|
||||||
|
Rom* rom) {
|
||||||
|
|
||||||
|
if (!rom || !rom->is_loaded()) {
|
||||||
|
return absl::FailedPreconditionError("ROM not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
zelda3::Overworld overworld(rom);
|
||||||
|
auto status = overworld.Load(rom);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply each change
|
||||||
|
for (const auto& change : proposal.changes) {
|
||||||
|
// Set the correct world
|
||||||
|
if (change.map_id < 0x40) {
|
||||||
|
overworld.set_current_world(0); // Light World
|
||||||
|
} else if (change.map_id < 0x80) {
|
||||||
|
overworld.set_current_world(1); // Dark World
|
||||||
|
} else {
|
||||||
|
overworld.set_current_world(2); // Special World
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the tile change
|
||||||
|
overworld.SetTile(change.x, change.y, change.new_tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: We don't save to disk here - that's the caller's responsibility
|
||||||
|
// This allows for sandbox testing before committing
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::StatusOr<gfx::Bitmap> Tile16ProposalGenerator::GenerateDiff(
|
||||||
|
const Tile16Proposal& /* proposal */,
|
||||||
|
Rom* /* before_rom */,
|
||||||
|
Rom* /* after_rom */) {
|
||||||
|
|
||||||
|
// TODO: Implement visual diff generation
|
||||||
|
// This would:
|
||||||
|
// 1. Load overworld from both ROMs
|
||||||
|
// 2. Render the affected regions
|
||||||
|
// 3. Create side-by-side or overlay comparison
|
||||||
|
// 4. Highlight changed tiles
|
||||||
|
|
||||||
|
return absl::UnimplementedError("Visual diff generation not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status Tile16ProposalGenerator::SaveProposal(
|
||||||
|
const Tile16Proposal& proposal,
|
||||||
|
const std::string& path) {
|
||||||
|
|
||||||
|
std::ofstream file(path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
absl::StrCat("Failed to open file for writing: ", path));
|
||||||
|
}
|
||||||
|
|
||||||
|
file << proposal.ToJson();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::StatusOr<Tile16Proposal> Tile16ProposalGenerator::LoadProposal(
|
||||||
|
const std::string& path) {
|
||||||
|
|
||||||
|
std::ifstream file(path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
absl::StrCat("Failed to open file for reading: ", path));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << file.rdbuf();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return Tile16Proposal::FromJson(buffer.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cli
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
147
src/cli/service/tile16_proposal_generator.h
Normal file
147
src/cli/service/tile16_proposal_generator.h
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
#ifndef YAZE_CLI_SERVICE_TILE16_PROPOSAL_GENERATOR_H_
|
||||||
|
#define YAZE_CLI_SERVICE_TILE16_PROPOSAL_GENERATOR_H_
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/status/status.h"
|
||||||
|
#include "absl/status/statusor.h"
|
||||||
|
#include "app/gfx/bitmap.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace cli {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a single tile16 change in a proposal.
|
||||||
|
*/
|
||||||
|
struct Tile16Change {
|
||||||
|
int map_id; // Overworld map ID
|
||||||
|
int x; // Tile16 X coordinate
|
||||||
|
int y; // Tile16 Y coordinate
|
||||||
|
uint16_t old_tile; // Original tile16 ID (for rollback)
|
||||||
|
uint16_t new_tile; // New tile16 ID to apply
|
||||||
|
|
||||||
|
std::string ToString() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a proposal for tile16 edits on the overworld.
|
||||||
|
*
|
||||||
|
* This is the core data structure for the accept/reject workflow.
|
||||||
|
* AI generates proposals, which are then applied to a sandbox ROM
|
||||||
|
* for preview before being committed to the main ROM.
|
||||||
|
*/
|
||||||
|
struct Tile16Proposal {
|
||||||
|
std::string id; // Unique proposal ID (UUID-like)
|
||||||
|
std::string prompt; // Original user prompt
|
||||||
|
std::vector<Tile16Change> changes; // List of tile changes
|
||||||
|
std::string reasoning; // AI's explanation
|
||||||
|
std::string ai_service; // "gemini", "ollama", "mock"
|
||||||
|
std::chrono::system_clock::time_point created_at; // Timestamp
|
||||||
|
|
||||||
|
// Proposal state
|
||||||
|
enum class Status {
|
||||||
|
PENDING, // Generated but not reviewed
|
||||||
|
ACCEPTED, // User accepted, changes applied
|
||||||
|
REJECTED, // User rejected, changes discarded
|
||||||
|
APPLIED // Successfully applied to ROM
|
||||||
|
};
|
||||||
|
Status status = Status::PENDING;
|
||||||
|
|
||||||
|
std::string ToJson() const;
|
||||||
|
static absl::StatusOr<Tile16Proposal> FromJson(const std::string& json);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generates and manages tile16 editing proposals.
|
||||||
|
*
|
||||||
|
* This class bridges the AI service with the overworld editing system,
|
||||||
|
* providing a safe sandbox workflow for reviewing and applying changes.
|
||||||
|
*/
|
||||||
|
class Tile16ProposalGenerator {
|
||||||
|
public:
|
||||||
|
Tile16ProposalGenerator() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate a tile16 proposal from an AI-generated command list.
|
||||||
|
*
|
||||||
|
* @param prompt The original user prompt
|
||||||
|
* @param commands List of commands from AI (e.g., "overworld set-tile ...")
|
||||||
|
* @param ai_service Name of the AI service used
|
||||||
|
* @param rom Reference ROM for validation
|
||||||
|
* @return Tile16Proposal with parsed changes
|
||||||
|
*/
|
||||||
|
absl::StatusOr<Tile16Proposal> GenerateFromCommands(
|
||||||
|
const std::string& prompt,
|
||||||
|
const std::vector<std::string>& commands,
|
||||||
|
const std::string& ai_service,
|
||||||
|
Rom* rom);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Apply a proposal to a ROM (typically a sandbox).
|
||||||
|
*
|
||||||
|
* This modifies the ROM in memory but doesn't save to disk.
|
||||||
|
* Used for preview and testing.
|
||||||
|
*
|
||||||
|
* @param proposal The proposal to apply
|
||||||
|
* @param rom The ROM to modify
|
||||||
|
* @return Status indicating success or failure
|
||||||
|
*/
|
||||||
|
absl::Status ApplyProposal(const Tile16Proposal& proposal, Rom* rom);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate a visual diff bitmap for a proposal.
|
||||||
|
*
|
||||||
|
* Creates a side-by-side or overlay comparison of before/after state.
|
||||||
|
*
|
||||||
|
* @param proposal The proposal to visualize
|
||||||
|
* @param before_rom ROM in original state
|
||||||
|
* @param after_rom ROM with proposal applied
|
||||||
|
* @return Bitmap showing the visual difference
|
||||||
|
*/
|
||||||
|
absl::StatusOr<gfx::Bitmap> GenerateDiff(
|
||||||
|
const Tile16Proposal& proposal,
|
||||||
|
Rom* before_rom,
|
||||||
|
Rom* after_rom);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save a proposal to a JSON file for later review.
|
||||||
|
*
|
||||||
|
* @param proposal The proposal to save
|
||||||
|
* @param path File path to save to
|
||||||
|
* @return Status indicating success or failure
|
||||||
|
*/
|
||||||
|
absl::Status SaveProposal(const Tile16Proposal& proposal,
|
||||||
|
const std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load a proposal from a JSON file.
|
||||||
|
*
|
||||||
|
* @param path File path to load from
|
||||||
|
* @return The loaded proposal or error
|
||||||
|
*/
|
||||||
|
absl::StatusOr<Tile16Proposal> LoadProposal(const std::string& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Parse a single "overworld set-tile" command into a Tile16Change.
|
||||||
|
*
|
||||||
|
* Expected format: "overworld set-tile --map 0 --x 10 --y 20 --tile 0x02E"
|
||||||
|
*/
|
||||||
|
absl::StatusOr<Tile16Change> ParseSetTileCommand(
|
||||||
|
const std::string& command,
|
||||||
|
Rom* rom);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate a unique proposal ID.
|
||||||
|
*/
|
||||||
|
std::string GenerateProposalId() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cli
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_CLI_SERVICE_TILE16_PROPOSAL_GENERATOR_H_
|
||||||
|
|
||||||
@@ -62,6 +62,10 @@ add_executable(
|
|||||||
cli/service/test_suite_writer.cc
|
cli/service/test_suite_writer.cc
|
||||||
cli/service/test_suite_writer.h
|
cli/service/test_suite_writer.h
|
||||||
cli/service/gemini_ai_service.cc
|
cli/service/gemini_ai_service.cc
|
||||||
|
cli/service/tile16_proposal_generator.h
|
||||||
|
cli/service/tile16_proposal_generator.cc
|
||||||
|
cli/service/resource_context_builder.h
|
||||||
|
cli/service/resource_context_builder.cc
|
||||||
app/rom.cc
|
app/rom.cc
|
||||||
app/core/project.cc
|
app/core/project.cc
|
||||||
app/core/asar_wrapper.cc
|
app/core/asar_wrapper.cc
|
||||||
@@ -84,26 +88,33 @@ if(YAZE_WITH_JSON)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# SSL/HTTPS Support (Required for Gemini API and future collaborative features)
|
# SSL/HTTPS Support (Optional - Required for Gemini API and collaborative features)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
option(YAZE_WITH_SSL "Build with OpenSSL support for HTTPS" ON)
|
# SSL is only enabled when building with gRPC+JSON (the full agent/testing suite)
|
||||||
if(YAZE_WITH_SSL OR YAZE_WITH_JSON)
|
# This ensures Windows builds without these dependencies still work
|
||||||
# Find OpenSSL on the system
|
if(YAZE_WITH_GRPC AND YAZE_WITH_JSON)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL)
|
||||||
|
|
||||||
# Define the SSL support macro for httplib
|
if(OpenSSL_FOUND)
|
||||||
target_compile_definitions(z3ed PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
|
# 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)
|
# Link OpenSSL libraries
|
||||||
|
target_link_libraries(z3ed PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||||
# On macOS, also enable Keychain cert support
|
|
||||||
if(APPLE)
|
# On macOS, also enable Keychain cert support
|
||||||
target_compile_definitions(z3ed PRIVATE CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
|
if(APPLE)
|
||||||
target_link_libraries(z3ed PRIVATE "-framework CoreFoundation" "-framework Security")
|
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 (required for Gemini API)")
|
||||||
|
else()
|
||||||
|
message(WARNING "OpenSSL not found - Gemini API will not work (Ollama will still function)")
|
||||||
|
message(STATUS " Install OpenSSL to enable Gemini: brew install openssl (macOS) or apt-get install libssl-dev (Linux)")
|
||||||
endif()
|
endif()
|
||||||
|
else()
|
||||||
message(STATUS "✓ SSL/HTTPS support enabled for z3ed")
|
message(STATUS "Building z3ed without gRPC/JSON - AI agent features disabled")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
|
|||||||
Reference in New Issue
Block a user