yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
general_commands.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <filesystem>
3#include <fstream>
4#include <optional>
5#include <sstream>
6#include <string>
7#include <utility>
8#include <vector>
9
10#include "absl/flags/declare.h"
11#include "absl/flags/flag.h"
12#include "absl/status/status.h"
13#include "absl/status/status.h"
14#include "absl/strings/ascii.h"
15#include "absl/strings/match.h"
16#include "absl/strings/str_cat.h"
17#include "absl/strings/str_format.h"
18#include "absl/strings/str_join.h"
19#include "absl/time/clock.h"
20#include "absl/time/time.h"
21#include "absl/strings/string_view.h"
22#include "core/project.h"
23#include "zelda3/dungeon/room.h"
25#include "cli/cli.h"
37#include "cli/cli.h"
38#include "util/macro.h"
39
40ABSL_DECLARE_FLAG(std::string, rom);
41ABSL_DECLARE_FLAG(std::string, ai_provider);
42
43namespace yaze {
44namespace cli {
45namespace agent {
46
47namespace {
48
50 std::optional<std::string> resource;
51 std::string format = "json";
52 std::optional<std::string> output_path;
53 std::string version = "0.1.0";
54 std::optional<std::string> last_updated;
55};
56
57
58// Helper to load project and labels if available
59absl::Status TryLoadProjectAndLabels(Rom& rom) {
60 // Try to find and load a project file in current directory
62 auto project_status = project.Open(".");
63
64 if (project_status.ok()) {
65 std::cout << "šŸ“‚ Loaded project: " << project.name << "\n";
66
67 // Initialize embedded labels (all default Zelda3 resource names)
68 auto labels_status = project.InitializeEmbeddedLabels();
69 if (labels_status.ok()) {
70 std::cout << "āœ… Embedded labels initialized (all Zelda3 resources available)\n";
71 }
72
73 // Load labels from project (either embedded or external)
74 if (!project.labels_filename.empty()) {
75 auto* label_mgr = rom.resource_label();
76 if (label_mgr && label_mgr->LoadLabels(project.labels_filename)) {
77 std::cout << "šŸ·ļø Loaded custom labels from: " << project.labels_filename << "\n";
78 }
79 } else if (!project.resource_labels.empty() || project.use_embedded_labels) {
80 // Use labels embedded in project or default Zelda3 labels
81 auto* label_mgr = rom.resource_label();
82 if (label_mgr) {
83 label_mgr->labels_ = project.resource_labels;
84 label_mgr->labels_loaded_ = true;
85 std::cout << "šŸ·ļø Using embedded Zelda3 labels (rooms, sprites, entrances, items, etc.)\n";
86 }
87 }
88 } else {
89 // No project found - use embedded defaults anyway
90 std::cout << "ā„¹ļø No project file found. Using embedded default Zelda3 labels.\n";
92 }
93
94 return absl::OkStatus();
95}
96
97absl::Status EnsureRomLoaded(Rom& rom, const std::string& command) {
98 if (rom.is_loaded()) {
99 return absl::OkStatus();
100 }
101
102 std::string rom_path = absl::GetFlag(FLAGS_rom);
103 if (rom_path.empty()) {
104 return absl::FailedPreconditionError(
105 absl::StrFormat(
106 "No ROM loaded. Pass --rom=<path> when running %s.\n"
107 "Example: z3ed %s --rom=zelda3.sfc",
108 command, command));
109 }
110
111 // Load the ROM
112 auto status = rom.LoadFromFile(rom_path);
113 if (!status.ok()) {
114 return absl::FailedPreconditionError(absl::StrFormat(
115 "Failed to load ROM from '%s': %s", rom_path, status.message()));
116 }
117
118 return absl::OkStatus();
119}
120
121absl::StatusOr<DescribeOptions> ParseDescribeArgs(
122 const std::vector<std::string>& args) {
123 DescribeOptions options;
124 for (size_t i = 0; i < args.size(); ++i) {
125 const std::string& token = args[i];
126 std::string flag = token;
127 std::optional<std::string> inline_value;
128
129 if (absl::StartsWith(token, "--")) {
130 auto eq_pos = token.find('=');
131 if (eq_pos != std::string::npos) {
132 flag = token.substr(0, eq_pos);
133 inline_value = token.substr(eq_pos + 1);
134 }
135 }
136
137 auto require_value =
138 [&](absl::string_view flag_name) -> absl::StatusOr<std::string> {
139 if (inline_value.has_value()) {
140 return *inline_value;
141 }
142 if (i + 1 >= args.size()) {
143 return absl::InvalidArgumentError(
144 absl::StrFormat("Flag %s requires a value", flag_name));
145 }
146 return args[++i];
147 };
148
149 if (flag == "--resource") {
150 ASSIGN_OR_RETURN(auto value, require_value("--resource"));
151 options.resource = std::move(value);
152 } else if (flag == "--format") {
153 ASSIGN_OR_RETURN(auto value, require_value("--format"));
154 options.format = std::move(value);
155 } else if (flag == "--output") {
156 ASSIGN_OR_RETURN(auto value, require_value("--output"));
157 options.output_path = std::move(value);
158 } else if (flag == "--version") {
159 ASSIGN_OR_RETURN(auto value, require_value("--version"));
160 options.version = std::move(value);
161 } else if (flag == "--last-updated") {
162 ASSIGN_OR_RETURN(auto value, require_value("--last-updated"));
163 options.last_updated = std::move(value);
164 } else {
165 return absl::InvalidArgumentError(
166 absl::StrFormat("Unknown flag for agent describe: %s", token));
167 }
168 }
169
170 options.format = absl::AsciiStrToLower(options.format);
171 if (options.format != "json" && options.format != "yaml") {
172 return absl::InvalidArgumentError("--format must be either json or yaml");
173 }
174
175 return options;
176}
177
178} // namespace
179
180absl::Status HandleRunCommand(const std::vector<std::string>& arg_vec,
181 Rom& rom) {
182 if (arg_vec.size() < 2 || arg_vec[0] != "--prompt") {
183 return absl::InvalidArgumentError("Usage: agent run --prompt <prompt>");
184 }
185 std::string prompt = arg_vec[1];
186
187 RETURN_IF_ERROR(EnsureRomLoaded(rom, "agent run --prompt \"<prompt>\""));
188
189 // Get commands from the AI service
190 auto ai_service = CreateAIService(); // Use service factory
191 auto response_or = ai_service->GenerateResponse(prompt);
192 if (!response_or.ok()) {
193 return response_or.status();
194 }
195 AgentResponse response = std::move(response_or.value());
196 if (response.commands.empty()) {
197 return absl::FailedPreconditionError(
198 "Agent response did not include any executable commands.");
199 }
200
201 std::string provider = absl::GetFlag(FLAGS_ai_provider);
202
204 request.prompt = prompt;
205 request.response = &response;
206 request.rom = &rom;
207 request.sandbox_label = "agent-run";
208 request.ai_provider = std::move(provider);
209
210 ASSIGN_OR_RETURN(auto proposal_result,
212
213 const auto& metadata = proposal_result.metadata;
214 std::filesystem::path proposal_dir = metadata.log_path.parent_path();
215
216 std::cout
217 << "āœ… Agent successfully planned and executed changes in a sandbox."
218 << std::endl;
219 std::cout << " Proposal ID: " << metadata.id << std::endl;
220 std::cout << " Sandbox ROM: " << metadata.sandbox_rom_path << std::endl;
221 std::cout << " Proposal dir: " << proposal_dir << std::endl;
222 std::cout << " Diff file: " << metadata.diff_path << std::endl;
223 std::cout << " Log file: " << metadata.log_path << std::endl;
224 std::cout << " Proposal JSON: " << proposal_result.proposal_json_path
225 << std::endl;
226 std::cout << " Commands executed: "
227 << proposal_result.executed_commands << std::endl;
228 std::cout << " Tile16 changes: " << proposal_result.change_count
229 << std::endl;
230 std::cout << "\nTo review the changes, run:\n";
231 std::cout << " z3ed agent diff --proposal-id " << metadata.id << std::endl;
232 std::cout << "\nTo accept the changes, run:\n";
233 std::cout << " z3ed agent accept --proposal-id " << metadata.id << std::endl;
234
235 return absl::OkStatus();
236}
237
238absl::Status HandlePlanCommand(const std::vector<std::string>& arg_vec) {
239 if (arg_vec.size() < 2 || arg_vec[0] != "--prompt") {
240 return absl::InvalidArgumentError("Usage: agent plan --prompt <prompt>");
241 }
242 std::string prompt = arg_vec[1];
243
244 auto ai_service = CreateAIService(); // Use service factory
245 auto response_or = ai_service->GenerateResponse(prompt);
246 if (!response_or.ok()) {
247 return response_or.status();
248 }
249 std::vector<std::string> commands = response_or.value().commands;
250
251 // Create a proposal from the commands
252 Tile16ProposalGenerator generator;
253 auto proposal_or =
254 generator.GenerateFromCommands(prompt, commands, "ollama", nullptr);
255 if (!proposal_or.ok()) {
256 return proposal_or.status();
257 }
258 auto proposal = proposal_or.value();
259
260 auto& registry = ProposalRegistry::Instance();
261 auto plans_dir = registry.RootDirectory() / "plans";
262 std::error_code ec;
263 std::filesystem::create_directories(plans_dir, ec);
264 if (ec) {
265 return absl::InternalError(absl::StrCat("Failed to create plans directory: ", ec.message()));
266 }
267
268 auto plan_path = plans_dir / (proposal.id + ".json");
269 auto save_status = generator.SaveProposal(proposal, plan_path.string());
270 if (!save_status.ok()) {
271 return save_status;
272 }
273
274 std::cout << "AI Agent Plan (Proposal ID: " << proposal.id << "):\n";
275 std::cout << proposal.ToJson() << std::endl;
276 std::cout << "\nāœ… Plan saved to: " << plan_path.string() << std::endl;
277
278 return absl::OkStatus();
279}
280
281absl::Status HandleDiffCommand(Rom& rom, const std::vector<std::string>& args) {
282 std::optional<std::string> proposal_id;
283 for (size_t i = 0; i < args.size(); ++i) {
284 const std::string& token = args[i];
285 if (absl::StartsWith(token, "--proposal-id=")) {
286 proposal_id = token.substr(14);
287 } else if (token == "--proposal-id" && i + 1 < args.size()) {
288 proposal_id = args[i + 1];
289 ++i;
290 }
291 }
292
293 auto& registry = ProposalRegistry::Instance();
294 absl::StatusOr<ProposalRegistry::ProposalMetadata> proposal_or;
295 if (proposal_id.has_value()) {
296 proposal_or = registry.GetProposal(proposal_id.value());
297 } else {
298 proposal_or = registry.GetLatestPendingProposal();
299 }
300
301 if (proposal_or.ok()) {
302 const auto& proposal = proposal_or.value();
303
304 std::cout << "\n=== Proposal Diff ===\n";
305 std::cout << "Proposal ID: " << proposal.id << "\n";
306 std::cout << "Sandbox ID: " << proposal.sandbox_id << "\n";
307 std::cout << "Prompt: " << proposal.prompt << "\n";
308 std::cout << "Description: " << proposal.description << "\n";
309 std::cout << "Status: ";
310 switch (proposal.status) {
312 std::cout << "Pending";
313 break;
315 std::cout << "Accepted";
316 break;
318 std::cout << "Rejected";
319 break;
320 }
321 std::cout << "\n";
322 std::cout << "Created: " << absl::FormatTime(proposal.created_at) << "\n";
323 std::cout << "Commands Executed: " << proposal.commands_executed << "\n";
324 std::cout << "Bytes Changed: " << proposal.bytes_changed << "\n\n";
325
326 if (!proposal.sandbox_rom_path.empty()) {
327 std::cout << "Sandbox ROM: " << proposal.sandbox_rom_path << "\n";
328 }
329 std::cout << "Proposal directory: "
330 << proposal.log_path.parent_path() << "\n";
331 std::cout << "Diff file: " << proposal.diff_path << "\n";
332 std::cout << "Log file: " << proposal.log_path << "\n\n";
333
334 if (std::filesystem::exists(proposal.diff_path)) {
335 std::cout << "--- Diff Content ---\n";
336 std::ifstream diff_file(proposal.diff_path);
337 if (diff_file.is_open()) {
338 std::string line;
339 while (std::getline(diff_file, line)) {
340 std::cout << line << "\n";
341 }
342 } else {
343 std::cout << "(Unable to read diff file)\n";
344 }
345 } else {
346 std::cout << "(No diff file found)\n";
347 }
348
349 std::cout << "\n--- Execution Log ---\n";
350 if (std::filesystem::exists(proposal.log_path)) {
351 std::ifstream log_file(proposal.log_path);
352 if (log_file.is_open()) {
353 std::string line;
354 int line_count = 0;
355 while (std::getline(log_file, line)) {
356 std::cout << line << "\n";
357 line_count++;
358 if (line_count > 50) {
359 std::cout << "... (log truncated, see " << proposal.log_path
360 << " for full output)\n";
361 break;
362 }
363 }
364 } else {
365 std::cout << "(Unable to read log file)\n";
366 }
367 } else {
368 std::cout << "(No log file found)\n";
369 }
370
371 std::cout << "\n=== Next Steps ===\n";
372 std::cout << "To accept changes: z3ed agent commit\n";
373 std::cout << "To reject changes: z3ed agent revert\n";
374 std::cout << "To review in GUI: yaze --proposal=" << proposal.id << "\n";
375
376 return absl::OkStatus();
377 }
378
379 if (rom.is_loaded()) {
380 auto sandbox_or = RomSandboxManager::Instance().ActiveSandbox();
381 if (!sandbox_or.ok()) {
382 return absl::NotFoundError(
383 "No pending proposals found and no active sandbox. Run 'z3ed agent "
384 "run' first.");
385 }
386 // TODO: Use new CommandHandler system for RomDiff
387 // Reference: src/app/rom.cc (Rom comparison methods)
388 auto status = absl::UnimplementedError("RomDiff not yet implemented in new CommandHandler system");
389 if (!status.ok()) {
390 return status;
391 }
392 } else {
393 return absl::AbortedError("No ROM loaded.");
394 }
395 return absl::OkStatus();
396}
397
398absl::Status HandleLearnCommand(const std::vector<std::string>& args) {
400 static bool initialized = false;
401
402 if (!initialized) {
403 auto status = learn_service.Initialize();
404 if (!status.ok()) {
405 std::cerr << "Failed to initialize learned knowledge service: "
406 << status.message() << std::endl;
407 return status;
408 }
409 initialized = true;
410 }
411
412 if (args.empty()) {
413 // Show usage
414 std::cout << "\nUsage: z3ed agent learn [options]\n\n";
415 std::cout << "Options:\n";
416 std::cout << " --preference <key>=<value> Set a preference\n";
417 std::cout << " --get-preference <key> Get a preference value\n";
418 std::cout << " --list-preferences List all preferences\n";
419 std::cout << " --pattern <type> --data <json> Learn a ROM pattern\n";
420 std::cout << " --query-patterns <type> Query learned patterns\n";
421 std::cout << " --project <name> --context <text> Save project context\n";
422 std::cout << " --get-project <name> Get project context\n";
423 std::cout << " --list-projects List all projects\n";
424 std::cout << " --memory <topic> --summary <text> Store conversation memory\n";
425 std::cout << " --search-memories <query> Search memories\n";
426 std::cout << " --recent-memories [limit] Show recent memories\n";
427 std::cout << " --export <file> Export all data to JSON\n";
428 std::cout << " --import <file> Import data from JSON\n";
429 std::cout << " --stats Show statistics\n";
430 std::cout << " --clear Clear all learned data\n";
431 return absl::OkStatus();
432 }
433
434 // Parse arguments
435 std::string command = args[0];
436
437 if (command == "--preference" && args.size() >= 2) {
438 std::string pref = args[1];
439 size_t eq_pos = pref.find('=');
440 if (eq_pos == std::string::npos) {
441 return absl::InvalidArgumentError("Preference must be in format key=value");
442 }
443 std::string key = pref.substr(0, eq_pos);
444 std::string value = pref.substr(eq_pos + 1);
445 auto status = learn_service.SetPreference(key, value);
446 if (status.ok()) {
447 std::cout << "āœ“ Preference '" << key << "' set to '" << value << "'\n";
448 }
449 return status;
450 }
451
452 if (command == "--get-preference" && args.size() >= 2) {
453 auto value = learn_service.GetPreference(args[1]);
454 if (value) {
455 std::cout << args[1] << " = " << *value << "\n";
456 } else {
457 std::cout << "Preference '" << args[1] << "' not found\n";
458 }
459 return absl::OkStatus();
460 }
461
462 if (command == "--list-preferences") {
463 auto prefs = learn_service.GetAllPreferences();
464 if (prefs.empty()) {
465 std::cout << "No preferences stored.\n";
466 } else {
467 std::cout << "\n=== Stored Preferences ===\n";
468 for (const auto& [key, value] : prefs) {
469 std::cout << " " << key << " = " << value << "\n";
470 }
471 }
472 return absl::OkStatus();
473 }
474
475 if (command == "--stats") {
476 auto stats = learn_service.GetStats();
477 std::cout << "\n=== Learned Knowledge Statistics ===\n";
478 std::cout << " Preferences: " << stats.preference_count << "\n";
479 std::cout << " ROM Patterns: " << stats.pattern_count << "\n";
480 std::cout << " Projects: " << stats.project_count << "\n";
481 std::cout << " Memories: " << stats.memory_count << "\n";
482 std::cout << " First learned: " << absl::FormatTime(absl::FromUnixMillis(stats.first_learned_at)) << "\n";
483 std::cout << " Last updated: " << absl::FormatTime(absl::FromUnixMillis(stats.last_updated_at)) << "\n";
484 return absl::OkStatus();
485 }
486
487 if (command == "--export" && args.size() >= 2) {
488 auto json = learn_service.ExportToJSON();
489 if (!json.ok()) {
490 return json.status();
491 }
492 std::ofstream file(args[1]);
493 if (!file.is_open()) {
494 return absl::InternalError("Failed to open file for writing");
495 }
496 file << *json;
497 std::cout << "āœ“ Exported learned data to " << args[1] << "\n";
498 return absl::OkStatus();
499 }
500
501 if (command == "--import" && args.size() >= 2) {
502 std::ifstream file(args[1]);
503 if (!file.is_open()) {
504 return absl::NotFoundError("File not found: " + args[1]);
505 }
506 std::stringstream buffer;
507 buffer << file.rdbuf();
508 auto status = learn_service.ImportFromJSON(buffer.str());
509 if (status.ok()) {
510 std::cout << "āœ“ Imported learned data from " << args[1] << "\n";
511 }
512 return status;
513 }
514
515 if (command == "--clear") {
516 auto status = learn_service.ClearAll();
517 if (status.ok()) {
518 std::cout << "āœ“ All learned data cleared\n";
519 }
520 return status;
521 }
522
523 if (command == "--list-projects") {
524 auto projects = learn_service.GetAllProjects();
525 if (projects.empty()) {
526 std::cout << "No projects stored.\n";
527 } else {
528 std::cout << "\n=== Stored Projects ===\n";
529 for (const auto& proj : projects) {
530 std::cout << " " << proj.project_name << "\n";
531 std::cout << " ROM Hash: " << proj.rom_hash.substr(0, 16) << "...\n";
532 std::cout << " Last Accessed: " << absl::FormatTime(absl::FromUnixMillis(proj.last_accessed)) << "\n";
533 }
534 }
535 return absl::OkStatus();
536 }
537
538 if (command == "--recent-memories") {
539 int limit = 10;
540 if (args.size() >= 2) {
541 limit = std::stoi(args[1]);
542 }
543 auto memories = learn_service.GetRecentMemories(limit);
544 if (memories.empty()) {
545 std::cout << "No memories stored.\n";
546 } else {
547 std::cout << "\n=== Recent Memories ===\n";
548 for (const auto& mem : memories) {
549 std::cout << " Topic: " << mem.topic << "\n";
550 std::cout << " Summary: " << mem.summary << "\n";
551 std::cout << " Facts: " << mem.key_facts.size() << " key facts\n";
552 std::cout << " Created: " << absl::FormatTime(absl::FromUnixMillis(mem.created_at)) << "\n";
553 std::cout << "\n";
554 }
555 }
556 return absl::OkStatus();
557 }
558
559 return absl::InvalidArgumentError("Unknown learn command. Use 'z3ed agent learn' for usage.");
560}
561
562absl::Status HandleListCommand() {
563 auto& registry = ProposalRegistry::Instance();
564 auto proposals = registry.ListProposals();
565
566 if (proposals.empty()) {
567 std::cout << "No proposals found.\n";
568 std::cout
569 << "Run 'z3ed agent run --prompt \"...\"' to create a proposal.\n";
570 return absl::OkStatus();
571 }
572
573 std::cout << "\n=== Agent Proposals ===\n\n";
574
575 for (const auto& proposal : proposals) {
576 std::cout << "ID: " << proposal.id << "\n";
577 std::cout << " Status: ";
578 switch (proposal.status) {
580 std::cout << "Pending";
581 break;
583 std::cout << "Accepted";
584 break;
586 std::cout << "Rejected";
587 break;
588 }
589 std::cout << "\n";
590 std::cout << " Created: " << absl::FormatTime(proposal.created_at) << "\n";
591 std::cout << " Prompt: " << proposal.prompt << "\n";
592 std::cout << " Commands: " << proposal.commands_executed << "\n";
593 std::cout << " Bytes Changed: " << proposal.bytes_changed << "\n";
594 std::cout << "\n";
595 }
596
597 std::cout << "Total: " << proposals.size() << " proposal(s)\n";
598 std::cout << "\nUse 'z3ed agent diff --proposal-id=<id>' to view details.\n";
599
600 return absl::OkStatus();
601}
602
603absl::Status HandleCommitCommand(Rom& rom) {
604 if (rom.is_loaded()) {
605 auto status = rom.SaveToFile({.save_new = false});
606 if (!status.ok()) {
607 return status;
608 }
609 std::cout << "āœ… Changes committed successfully." << std::endl;
610 } else {
611 return absl::AbortedError("No ROM loaded.");
612 }
613 return absl::OkStatus();
614}
615
616absl::Status HandleRevertCommand(Rom& rom) {
617 if (rom.is_loaded()) {
618 auto status = rom.LoadFromFile(rom.filename());
619 if (!status.ok()) {
620 return status;
621 }
622 std::cout << "āœ… Changes reverted successfully." << std::endl;
623 } else {
624 return absl::AbortedError("No ROM loaded.");
625 }
626 return absl::OkStatus();
627}
628
629absl::Status HandleDescribeCommand(const std::vector<std::string>& arg_vec) {
630 ASSIGN_OR_RETURN(auto options, ParseDescribeArgs(arg_vec));
631
632 const auto& catalog = ResourceCatalog::Instance();
633 std::optional<ResourceSchema> resource_schema;
634 if (options.resource.has_value()) {
635 auto resource_or = catalog.GetResource(*options.resource);
636 if (!resource_or.ok()) {
637 return resource_or.status();
638 }
639 resource_schema = resource_or.value();
640 }
641
642 std::string payload;
643 if (options.format == "json") {
644 if (resource_schema.has_value()) {
645 payload = catalog.SerializeResource(*resource_schema);
646 } else {
647 payload = catalog.SerializeResources(catalog.AllResources());
648 }
649 } else {
650 std::string last_updated =
651 options.last_updated.has_value()
652 ? *options.last_updated
653 : absl::FormatTime("%Y-%m-%d", absl::Now(), absl::LocalTimeZone());
654 if (resource_schema.has_value()) {
655 std::vector<ResourceSchema> schemas{*resource_schema};
656 payload = catalog.SerializeResourcesAsYaml(schemas, options.version,
657 last_updated);
658 } else {
659 payload = catalog.SerializeResourcesAsYaml(catalog.AllResources(),
660 options.version, last_updated);
661 }
662 }
663
664 if (options.output_path.has_value()) {
665 std::ofstream out(*options.output_path, std::ios::binary | std::ios::trunc);
666 if (!out.is_open()) {
667 return absl::InternalError(absl::StrFormat(
668 "Failed to open %s for writing", *options.output_path));
669 }
670 out << payload;
671 out.close();
672 if (!out) {
673 return absl::InternalError(absl::StrFormat("Failed to write schema to %s",
674 *options.output_path));
675 }
676 std::cout << absl::StrFormat("Wrote %s schema to %s", options.format,
677 *options.output_path)
678 << std::endl;
679 return absl::OkStatus();
680 }
681
682 std::cout << payload << std::endl;
683 return absl::OkStatus();
684}
685
686absl::Status HandleAcceptCommand(const std::vector<std::string>& arg_vec,
687 Rom& rom) {
688 std::optional<std::string> proposal_id;
689 for (size_t i = 0; i < arg_vec.size(); ++i) {
690 const std::string& token = arg_vec[i];
691 if (absl::StartsWith(token, "--proposal-id=")) {
692 proposal_id = token.substr(14);
693 break;
694 }
695 if (token == "--proposal-id" && i + 1 < arg_vec.size()) {
696 proposal_id = arg_vec[i + 1];
697 break;
698 }
699 }
700
701 if (!proposal_id.has_value() || proposal_id->empty()) {
702 return absl::InvalidArgumentError(
703 "Usage: agent accept --proposal-id <proposal_id>");
704 }
705
706 auto& registry = ProposalRegistry::Instance();
707 ASSIGN_OR_RETURN(auto metadata, registry.GetProposal(*proposal_id));
708
709 if (metadata.status == ProposalRegistry::ProposalStatus::kAccepted) {
710 std::cout << "Proposal '" << *proposal_id << "' is already accepted."
711 << std::endl;
712 return absl::OkStatus();
713 }
714
715 if (metadata.sandbox_rom_path.empty()) {
716 return absl::FailedPreconditionError(absl::StrCat(
717 "Proposal '", *proposal_id,
718 "' is missing sandbox ROM metadata. Cannot accept."));
719 }
720
721 if (!std::filesystem::exists(metadata.sandbox_rom_path)) {
722 return absl::NotFoundError(absl::StrCat(
723 "Sandbox ROM not found at ", metadata.sandbox_rom_path.string()));
724 }
725
727 EnsureRomLoaded(rom, "agent accept --proposal-id <proposal_id>"));
728
729 Rom sandbox_rom;
730 auto sandbox_load_status = sandbox_rom.LoadFromFile(
731 metadata.sandbox_rom_path.string(), RomLoadOptions::CliDefaults());
732 if (!sandbox_load_status.ok()) {
733 return absl::InternalError(absl::StrCat(
734 "Failed to load sandbox ROM: ", sandbox_load_status.message()));
735 }
736
737 if (rom.size() != sandbox_rom.size()) {
738 rom.Expand(static_cast<int>(sandbox_rom.size()));
739 }
740
741 auto copy_status = rom.WriteVector(0, sandbox_rom.vector());
742 if (!copy_status.ok()) {
743 return absl::InternalError(absl::StrCat(
744 "Failed to copy sandbox ROM data: ", copy_status.message()));
745 }
746
747 auto save_status = rom.SaveToFile({.save_new = false});
748 if (!save_status.ok()) {
749 return absl::InternalError(absl::StrCat(
750 "Failed to save changes to main ROM: ", save_status.message()));
751 }
752
753 RETURN_IF_ERROR(registry.UpdateStatus(
755 RETURN_IF_ERROR(registry.AppendLog(
756 *proposal_id,
757 absl::StrCat("Proposal accepted and applied to ", rom.filename())));
758
759 if (!metadata.sandbox_id.empty()) {
760 auto remove_status =
761 RomSandboxManager::Instance().RemoveSandbox(metadata.sandbox_id);
762 if (!remove_status.ok()) {
763 std::cerr << "Warning: Failed to remove sandbox '" << metadata.sandbox_id
764 << "': " << remove_status.message() << "\n";
765 }
766 }
767
768 std::cout << "āœ… Proposal '" << *proposal_id << "' accepted and applied to '"
769 << rom.filename() << "'." << std::endl;
770 std::cout << " Source sandbox ROM: " << metadata.sandbox_rom_path
771 << std::endl;
772
773 return absl::OkStatus();
774}
775
776} // namespace agent
777} // namespace cli
778} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:74
project::ResourceLabelManager * resource_label()
Definition rom.h:223
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:292
auto filename() const
Definition rom.h:211
auto vector() const
Definition rom.h:210
absl::Status WriteVector(int addr, std::vector< uint8_t > data)
Definition rom.cc:780
void Expand(int size)
Definition rom.h:105
absl::Status SaveToFile(const SaveSettings &settings)
Definition rom.cc:539
auto size() const
Definition rom.h:205
bool is_loaded() const
Definition rom.h:200
static ProposalRegistry & Instance()
static const ResourceCatalog & Instance()
absl::Status RemoveSandbox(const std::string &id)
absl::StatusOr< SandboxMetadata > ActiveSandbox() const
static RomSandboxManager & Instance()
Generates and manages tile16 editing proposals.
absl::StatusOr< Tile16Proposal > GenerateFromCommands(const std::string &prompt, const std::vector< std::string > &commands, const std::string &ai_service, Rom *rom)
Generate a tile16 proposal from an AI-generated command list.
absl::Status SaveProposal(const Tile16Proposal &proposal, const std::string &path)
Save a proposal to a JSON file for later review.
Manages persistent learned information across agent sessions.
std::optional< std::string > GetPreference(const std::string &key) const
std::vector< ProjectContext > GetAllProjects() const
std::vector< ConversationMemory > GetRecentMemories(int limit=10) const
absl::Status ImportFromJSON(const std::string &json_data)
absl::Status SetPreference(const std::string &key, const std::string &value)
absl::StatusOr< std::string > ExportToJSON() const
std::map< std::string, std::string > GetAllPreferences() const
ABSL_DECLARE_FLAG(std::string, rom)
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:61
absl::Status EnsureRomLoaded(Rom &rom, const std::string &command)
absl::StatusOr< DescribeOptions > ParseDescribeArgs(const std::vector< std::string > &args)
absl::Status HandleLearnCommand(const std::vector< std::string > &args)
absl::StatusOr< ProposalCreationResult > CreateProposalFromAgentResponse(const ProposalCreationRequest &request)
absl::Status HandleAcceptCommand(const std::vector< std::string > &args, Rom &rom)
absl::Status HandleRunCommand(const std::vector< std::string > &args, Rom &rom)
absl::Status HandleCommitCommand(Rom &rom)
absl::Status HandleDescribeCommand(const std::vector< std::string > &args)
absl::Status HandleDiffCommand(Rom &rom, const std::vector< std::string > &args)
absl::Status HandleRevertCommand(Rom &rom)
absl::Status HandleListCommand()
absl::Status HandlePlanCommand(const std::vector< std::string > &args)
std::unique_ptr< AIService > CreateAIService()
Main namespace for the application.
Definition controller.cc:20
static RomLoadOptions CliDefaults()
Definition rom.cc:52
std::vector< std::string > commands
Definition common.h:26
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > labels_
Definition project.h:235
Modern project structure with comprehensive settings consolidation.
Definition project.h:78
absl::Status InitializeEmbeddedLabels()
Definition project.cc:887
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > resource_labels
Definition project.h:100
std::string labels_filename
Definition project.h:94
absl::Status Open(const std::string &project_path)
Definition project.cc:115