Refactor CLI Service Structure and Enhance AI Integration

- Restructured CLI service source files to improve organization, moving files into dedicated directories for better maintainability.
- Introduced new AI service components, including `AIService`, `MockAIService`, and `GeminiAIService`, to facilitate natural language command generation.
- Implemented `PolicyEvaluator` and `ProposalRegistry` for enhanced proposal management and policy enforcement in AI workflows.
- Updated CMake configurations to reflect new file paths and ensure proper linking of the restructured components.
- Enhanced test suite with new test workflow generation capabilities, improving the robustness of automated testing.

This commit significantly advances the architecture of the z3ed system, laying the groundwork for more sophisticated AI-driven features and streamlined development processes.
This commit is contained in:
scawful
2025-10-03 09:54:27 -04:00
parent b89dcca93f
commit 90ddc3d50c
45 changed files with 224 additions and 167 deletions

View File

@@ -0,0 +1,482 @@
#include "cli/service/resources/resource_catalog.h"
#include <sstream>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
namespace yaze {
namespace cli {
namespace {
ResourceSchema MakePaletteSchema() {
ResourceSchema schema;
schema.resource = "palette";
schema.description =
"Palette manipulation commands covering export, import, and color editing.";
ResourceAction export_action;
export_action.name = "export";
export_action.synopsis = "z3ed palette export --group <group> --id <id> --to <file>";
export_action.stability = "experimental";
export_action.arguments = {
ResourceArgument{"--group", "integer", true, "Palette group id (0-31)."},
ResourceArgument{"--id", "integer", true, "Palette index inside the group."},
ResourceArgument{"--to", "path", true, "Destination file path for binary export."},
};
export_action.effects = {
"Reads ROM palette buffer and writes binary palette data to disk."};
ResourceAction import_action;
import_action.name = "import";
import_action.synopsis =
"z3ed palette import --group <group> --id <id> --from <file>";
import_action.stability = "experimental";
import_action.arguments = {
ResourceArgument{"--group", "integer", true, "Palette group id (0-31)."},
ResourceArgument{"--id", "integer", true, "Palette index inside the group."},
ResourceArgument{"--from", "path", true, "Source binary palette file."},
};
import_action.effects = {
"Writes imported palette bytes into ROM buffer and marks project dirty."};
schema.actions = {export_action, import_action};
return schema;
}
ResourceSchema MakeRomSchema() {
ResourceSchema schema;
schema.resource = "rom";
schema.description = "ROM validation, diffing, and snapshot helpers.";
ResourceAction info_action;
info_action.name = "info";
info_action.synopsis = "z3ed rom info --rom <file>";
info_action.stability = "stable";
info_action.arguments = {
ResourceArgument{"--rom", "path", true, "Path to ROM file configured via global flag."},
};
info_action.effects = {
"Reads ROM from disk and displays basic information (title, size, filename)."};
info_action.returns = {
{"title", "string", "ROM internal title from header."},
{"size", "integer", "ROM file size in bytes."},
{"filename", "string", "Full path to the ROM file."}};
ResourceAction validate_action;
validate_action.name = "validate";
validate_action.synopsis = "z3ed rom validate --rom <file>";
validate_action.stability = "stable";
validate_action.arguments = {
ResourceArgument{"--rom", "path", true, "Path to ROM file configured via global flag."},
};
validate_action.effects = {
"Reads ROM from disk, verifies checksum, and reports header status."};
validate_action.returns = {
{"report", "object",
"Structured validation summary with checksum and header results."}};
ResourceAction diff_action;
diff_action.name = "diff";
diff_action.synopsis = "z3ed rom diff <rom_a> <rom_b>";
diff_action.stability = "beta";
diff_action.arguments = {
ResourceArgument{"rom_a", "path", true, "Reference ROM path."},
ResourceArgument{"rom_b", "path", true, "Candidate ROM path."},
};
diff_action.effects = {
"Reads two ROM images, compares bytes, and streams differences to stdout."};
diff_action.returns = {
{"differences", "integer", "Count of mismatched bytes between ROMs."}};
ResourceAction generate_action;
generate_action.name = "generate-golden";
generate_action.synopsis =
"z3ed rom generate-golden <rom_file> <golden_file>";
generate_action.stability = "experimental";
generate_action.arguments = {
ResourceArgument{"rom_file", "path", true, "Source ROM to snapshot."},
ResourceArgument{"golden_file", "path", true, "Output path for golden image."},
};
generate_action.effects = {
"Writes out exact ROM image for tooling baselines and diff workflows."};
generate_action.returns = {
{"artifact", "path", "Absolute path to the generated golden image."}};
schema.actions = {info_action, validate_action, diff_action, generate_action};
return schema;
}
ResourceSchema MakePatchSchema() {
ResourceSchema schema;
schema.resource = "patch";
schema.description =
"Patch authoring and application commands covering BPS and Asar flows.";
ResourceAction apply_action;
apply_action.name = "apply";
apply_action.synopsis = "z3ed patch apply <rom_file> <bps_patch>";
apply_action.stability = "beta";
apply_action.arguments = {
ResourceArgument{"rom_file", "path", true,
"Source ROM image that will receive the patch."},
ResourceArgument{"bps_patch", "path", true,
"BPS patch to apply to the ROM."},
};
apply_action.effects = {
"Loads ROM from disk, applies a BPS patch, and writes `patched.sfc`."};
apply_action.returns = {
{"artifact", "path",
"Absolute path to the patched ROM image produced on success."}};
ResourceAction asar_action;
asar_action.name = "apply-asar";
asar_action.synopsis = "z3ed patch apply-asar <patch.asm>";
asar_action.stability = "prototype";
asar_action.arguments = {
ResourceArgument{"patch.asm", "path", true,
"Assembly patch consumed by the bundled Asar runtime."},
ResourceArgument{"--rom", "path", false,
"ROM path supplied via global --rom flag."},
};
asar_action.effects = {
"Invokes Asar against the active ROM buffer and applies assembled changes."};
asar_action.returns = {
{"log", "string", "Assembler diagnostics emitted during application."}};
ResourceAction create_action;
create_action.name = "create";
create_action.synopsis =
"z3ed patch create --source <rom> --target <rom> --out <patch.bps>";
create_action.stability = "experimental";
create_action.arguments = {
ResourceArgument{"--source", "path", true,
"Baseline ROM used when computing the patch."},
ResourceArgument{"--target", "path", true,
"Modified ROM to diff against the baseline."},
ResourceArgument{"--out", "path", true,
"Output path for the generated BPS patch."},
};
create_action.effects = {
"Compares source and target images to synthesize a distributable BPS patch."};
create_action.returns = {
{"artifact", "path", "File system path to the generated patch."}};
schema.actions = {apply_action, asar_action, create_action};
return schema;
}
ResourceSchema MakeOverworldSchema() {
ResourceSchema schema;
schema.resource = "overworld";
schema.description = "Overworld tile inspection and manipulation commands.";
ResourceAction get_tile;
get_tile.name = "get-tile";
get_tile.synopsis = "z3ed overworld get-tile --map <map_id> --x <x> --y <y>";
get_tile.stability = "stable";
get_tile.arguments = {
ResourceArgument{"--map", "integer", true, "Overworld map identifier (0-63)."},
ResourceArgument{"--x", "integer", true, "Tile x coordinate."},
ResourceArgument{"--y", "integer", true, "Tile y coordinate."},
};
get_tile.returns = {
{"tile", "integer",
"Tile id located at the supplied coordinates."}};
ResourceAction set_tile;
set_tile.name = "set-tile";
set_tile.synopsis =
"z3ed overworld set-tile --map <map_id> --x <x> --y <y> --tile <tile_id>";
set_tile.stability = "experimental";
set_tile.arguments = {
ResourceArgument{"--map", "integer", true, "Overworld map identifier (0-63)."},
ResourceArgument{"--x", "integer", true, "Tile x coordinate."},
ResourceArgument{"--y", "integer", true, "Tile y coordinate."},
ResourceArgument{"--tile", "integer", true, "Tile id to write."},
};
set_tile.effects = {
"Mutates overworld tile map and enqueues render invalidation."};
schema.actions = {get_tile, set_tile};
return schema;
}
ResourceSchema MakeDungeonSchema() {
ResourceSchema schema;
schema.resource = "dungeon";
schema.description = "Dungeon room export and inspection utilities.";
ResourceAction export_action;
export_action.name = "export";
export_action.synopsis = "z3ed dungeon export <room_id>";
export_action.stability = "prototype";
export_action.arguments = {
ResourceArgument{"room_id", "integer", true,
"Dungeon room identifier to inspect."},
};
export_action.effects = {
"Loads the active ROM via --rom and prints metadata for the requested room."};
export_action.returns = {
{"metadata", "object",
"Structured room summary including blockset, spriteset, palette, and layout."}};
ResourceAction list_objects_action;
list_objects_action.name = "list-objects";
list_objects_action.synopsis = "z3ed dungeon list-objects <room_id>";
list_objects_action.stability = "prototype";
list_objects_action.arguments = {
ResourceArgument{"room_id", "integer", true,
"Dungeon room identifier whose objects should be listed."},
};
list_objects_action.effects = {
"Streams parsed dungeon object records for the requested room to stdout."};
list_objects_action.returns = {
{"objects", "array",
"Collection of tile object records with ids, coordinates, and layers."}};
schema.actions = {export_action, list_objects_action};
return schema;
}
ResourceSchema MakeAgentSchema() {
ResourceSchema schema;
schema.resource = "agent";
schema.description =
"Agent workflow helpers including planning, diffing, listing, and schema discovery.";
ResourceAction describe_action;
describe_action.name = "describe";
describe_action.synopsis = "z3ed agent describe --resource <name>";
describe_action.stability = "prototype";
describe_action.arguments = {
ResourceArgument{"--resource", "string", false,
"Optional resource name to filter results."},
};
describe_action.returns = {
{"schema", "object",
"JSON schema describing resource arguments and semantics."}};
ResourceAction list_action;
list_action.name = "list";
list_action.synopsis = "z3ed agent list";
list_action.stability = "prototype";
list_action.arguments = {};
list_action.effects = {{"reads", "proposal_registry"}};
list_action.returns = {
{"proposals", "array",
"List of all proposals with ID, status, prompt, and metadata."}};
ResourceAction diff_action;
diff_action.name = "diff";
diff_action.synopsis = "z3ed agent diff [--proposal-id <id>]";
diff_action.stability = "prototype";
diff_action.arguments = {
ResourceArgument{"--proposal-id", "string", false,
"Optional proposal ID to view specific proposal. Defaults to latest pending."},
};
diff_action.effects = {{"reads", "proposal_registry"}, {"reads", "sandbox"}};
diff_action.returns = {
{"diff", "string", "Unified diff showing changes to ROM."},
{"log", "string", "Execution log of commands run."},
{"metadata", "object", "Proposal metadata including status and timestamps."}};
schema.actions = {describe_action, list_action, diff_action};
return schema;
}
} // namespace
const ResourceCatalog& ResourceCatalog::Instance() {
static ResourceCatalog* instance = new ResourceCatalog();
return *instance;
}
ResourceCatalog::ResourceCatalog()
: resources_({MakeRomSchema(), MakePatchSchema(), MakePaletteSchema(),
MakeOverworldSchema(), MakeDungeonSchema(), MakeAgentSchema()}) {}
absl::StatusOr<ResourceSchema> ResourceCatalog::GetResource(absl::string_view name) const {
for (const auto& resource : resources_) {
if (resource.resource == name) {
return resource;
}
}
return absl::NotFoundError(absl::StrCat("Resource not found: ", name));
}
const std::vector<ResourceSchema>& ResourceCatalog::AllResources() const { return resources_; }
std::string ResourceCatalog::SerializeResource(const ResourceSchema& schema) const {
return SerializeResources({schema});
}
std::string ResourceCatalog::SerializeResources(const std::vector<ResourceSchema>& schemas) const {
std::vector<std::string> entries;
entries.reserve(schemas.size());
for (const auto& resource : schemas) {
std::vector<std::string> action_entries;
action_entries.reserve(resource.actions.size());
for (const auto& action : resource.actions) {
std::vector<std::string> arg_entries;
arg_entries.reserve(action.arguments.size());
for (const auto& arg : action.arguments) {
arg_entries.push_back(absl::StrCat(
"{\"flag\":\"", EscapeJson(arg.flag),
"\",\"type\":\"", EscapeJson(arg.type),
"\",\"required\":", arg.required ? "true" : "false",
",\"description\":\"", EscapeJson(arg.description), "\"}"));
}
std::vector<std::string> effect_entries;
effect_entries.reserve(action.effects.size());
for (const auto& effect : action.effects) {
effect_entries.push_back(absl::StrCat("\"", EscapeJson(effect), "\""));
}
std::vector<std::string> return_entries;
return_entries.reserve(action.returns.size());
for (const auto& ret : action.returns) {
return_entries.push_back(absl::StrCat(
"{\"field\":\"", EscapeJson(ret.field),
"\",\"type\":\"", EscapeJson(ret.type),
"\",\"description\":\"", EscapeJson(ret.description), "\"}"));
}
action_entries.push_back(absl::StrCat(
"{\"name\":\"", EscapeJson(action.name),
"\",\"synopsis\":\"", EscapeJson(action.synopsis),
"\",\"stability\":\"", EscapeJson(action.stability),
"\",\"arguments\":[", absl::StrJoin(arg_entries, ","), "],",
"\"effects\":[", absl::StrJoin(effect_entries, ","), "],",
"\"returns\":[", absl::StrJoin(return_entries, ","), "]}"));
}
entries.push_back(absl::StrCat(
"{\"resource\":\"", EscapeJson(resource.resource),
"\",\"description\":\"", EscapeJson(resource.description),
"\",\"actions\":[", absl::StrJoin(action_entries, ","), "]}"));
}
return absl::StrCat("{\"resources\":[", absl::StrJoin(entries, ","), "]}");
}
std::string ResourceCatalog::EscapeJson(absl::string_view value) {
std::string out;
out.reserve(value.size());
for (char c : value) {
switch (c) {
case '\\':
out += "\\\\";
break;
case '\"':
out += "\\\"";
break;
case '\n':
out += "\\n";
break;
case '\r':
out += "\\r";
break;
case '\t':
out += "\\t";
break;
default:
out += c;
break;
}
}
return out;
}
std::string ResourceCatalog::SerializeResourcesAsYaml(
const std::vector<ResourceSchema>& schemas,
absl::string_view version,
absl::string_view last_updated) const {
std::string out;
absl::StrAppend(&out, "# Auto-generated resource catalogue\n");
absl::StrAppend(&out, "version: ", EscapeYaml(version), "\n");
absl::StrAppend(&out, "last_updated: ", EscapeYaml(last_updated), "\n");
absl::StrAppend(&out, "resources:\n");
for (const auto& resource : schemas) {
absl::StrAppend(&out, " - name: ", EscapeYaml(resource.resource), "\n");
absl::StrAppend(&out, " description: ", EscapeYaml(resource.description), "\n");
if (resource.actions.empty()) {
absl::StrAppend(&out, " actions: []\n");
continue;
}
absl::StrAppend(&out, " actions:\n");
for (const auto& action : resource.actions) {
absl::StrAppend(&out, " - name: ", EscapeYaml(action.name), "\n");
absl::StrAppend(&out, " synopsis: ", EscapeYaml(action.synopsis), "\n");
absl::StrAppend(&out, " stability: ", EscapeYaml(action.stability), "\n");
if (action.arguments.empty()) {
absl::StrAppend(&out, " args: []\n");
} else {
absl::StrAppend(&out, " args:\n");
for (const auto& arg : action.arguments) {
absl::StrAppend(&out, " - flag: ", EscapeYaml(arg.flag), "\n");
absl::StrAppend(&out, " type: ", EscapeYaml(arg.type), "\n");
absl::StrAppend(&out, " required: ", arg.required ? "true\n" : "false\n");
absl::StrAppend(&out, " description: ", EscapeYaml(arg.description), "\n");
}
}
if (action.effects.empty()) {
absl::StrAppend(&out, " effects: []\n");
} else {
absl::StrAppend(&out, " effects:\n");
for (const auto& effect : action.effects) {
absl::StrAppend(&out, " - ", EscapeYaml(effect), "\n");
}
}
if (action.returns.empty()) {
absl::StrAppend(&out, " returns: []\n");
} else {
absl::StrAppend(&out, " returns:\n");
for (const auto& ret : action.returns) {
absl::StrAppend(&out, " - field: ", EscapeYaml(ret.field), "\n");
absl::StrAppend(&out, " type: ", EscapeYaml(ret.type), "\n");
absl::StrAppend(&out, " description: ", EscapeYaml(ret.description), "\n");
}
}
}
}
return out;
}
std::string ResourceCatalog::EscapeYaml(absl::string_view value) {
std::string out;
out.reserve(value.size() + 2);
out.push_back('"');
for (char c : value) {
switch (c) {
case '\\':
out += "\\\\";
break;
case '"':
out += "\\\"";
break;
case '\n':
out += "\\n";
break;
case '\r':
out += "\\r";
break;
case '\t':
out += "\\t";
break;
default:
out.push_back(c);
break;
}
}
out.push_back('"');
return out;
}
} // namespace cli
} // namespace yaze