refactor: Update CMake configurations and enhance TODO management features
- Replaced `${CMAKE_SOURCE_DIR}` with `${PROJECT_SOURCE_DIR}` in CMake files for consistent path handling.
- Introduced new `todo_commands` and `todo_manager` components to manage TODO items, including creation, listing, updating, and deletion functionalities.
- Added detailed implementations for handling TODO commands in the CLI, improving task management capabilities.
- Enhanced the `TodoManager` class to support persistence and execution planning for TODO items.
- Updated CMake configurations to include new source files related to TODO management, ensuring proper integration into the build system.
This commit is contained in:
423
src/cli/service/agent/todo_manager.cc
Normal file
423
src/cli/service/agent/todo_manager.cc
Normal file
@@ -0,0 +1,423 @@
|
||||
#include "cli/service/agent/todo_manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "util/platform_paths.h"
|
||||
|
||||
#ifdef YAZE_WITH_JSON
|
||||
#include "nlohmann/json.hpp"
|
||||
using json = nlohmann::json;
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace agent {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string CurrentTimestamp() {
|
||||
auto now = absl::Now();
|
||||
return absl::FormatTime("%Y-%m-%d %H:%M:%S", now, absl::LocalTimeZone());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string TodoItem::StatusToString() const {
|
||||
switch (status) {
|
||||
case Status::PENDING: return "pending";
|
||||
case Status::IN_PROGRESS: return "in_progress";
|
||||
case Status::COMPLETED: return "completed";
|
||||
case Status::BLOCKED: return "blocked";
|
||||
case Status::CANCELLED: return "cancelled";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
TodoItem::Status TodoItem::StringToStatus(const std::string& str) {
|
||||
if (str == "pending") return Status::PENDING;
|
||||
if (str == "in_progress") return Status::IN_PROGRESS;
|
||||
if (str == "completed") return Status::COMPLETED;
|
||||
if (str == "blocked") return Status::BLOCKED;
|
||||
if (str == "cancelled") return Status::CANCELLED;
|
||||
return Status::PENDING;
|
||||
}
|
||||
|
||||
TodoManager::TodoManager() {
|
||||
auto result = util::PlatformPaths::GetAppDataSubdirectory("agent");
|
||||
if (result.ok()) {
|
||||
data_dir_ = *result;
|
||||
} else {
|
||||
data_dir_ = std::filesystem::current_path() / ".yaze" / "agent";
|
||||
}
|
||||
todos_file_ = (std::filesystem::path(data_dir_) / "todos.json").string();
|
||||
}
|
||||
|
||||
TodoManager::TodoManager(const std::string& data_dir)
|
||||
: data_dir_(data_dir),
|
||||
todos_file_((std::filesystem::path(data_dir) / "todos.json").string()) {}
|
||||
|
||||
absl::Status TodoManager::Initialize() {
|
||||
auto status = util::PlatformPaths::EnsureDirectoryExists(data_dir_);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Try to load existing TODOs
|
||||
if (util::PlatformPaths::Exists(todos_file_)) {
|
||||
return Load();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::string TodoManager::GenerateId() {
|
||||
return absl::StrFormat("todo_%d", next_id_++);
|
||||
}
|
||||
|
||||
std::string TodoManager::GetTimestamp() const {
|
||||
return CurrentTimestamp();
|
||||
}
|
||||
|
||||
absl::StatusOr<TodoItem> TodoManager::CreateTodo(
|
||||
const std::string& description,
|
||||
const std::string& category,
|
||||
int priority) {
|
||||
TodoItem item;
|
||||
item.id = GenerateId();
|
||||
item.description = description;
|
||||
item.category = category;
|
||||
item.priority = priority;
|
||||
item.status = TodoItem::Status::PENDING;
|
||||
item.created_at = GetTimestamp();
|
||||
item.updated_at = item.created_at;
|
||||
|
||||
todos_.push_back(item);
|
||||
|
||||
auto status = Save();
|
||||
if (!status.ok()) {
|
||||
todos_.pop_back(); // Rollback
|
||||
return status;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
absl::Status TodoManager::UpdateTodo(const std::string& id, const TodoItem& item) {
|
||||
auto it = std::find_if(todos_.begin(), todos_.end(),
|
||||
[&id](const TodoItem& t) { return t.id == id; });
|
||||
|
||||
if (it == todos_.end()) {
|
||||
return absl::NotFoundError(absl::StrFormat("TODO with ID %s not found", id));
|
||||
}
|
||||
|
||||
TodoItem updated = item;
|
||||
updated.id = id; // Preserve ID
|
||||
updated.updated_at = GetTimestamp();
|
||||
|
||||
*it = updated;
|
||||
return Save();
|
||||
}
|
||||
|
||||
absl::Status TodoManager::UpdateStatus(const std::string& id, TodoItem::Status status) {
|
||||
auto it = std::find_if(todos_.begin(), todos_.end(),
|
||||
[&id](const TodoItem& t) { return t.id == id; });
|
||||
|
||||
if (it == todos_.end()) {
|
||||
return absl::NotFoundError(absl::StrFormat("TODO with ID %s not found", id));
|
||||
}
|
||||
|
||||
it->status = status;
|
||||
it->updated_at = GetTimestamp();
|
||||
|
||||
return Save();
|
||||
}
|
||||
|
||||
absl::StatusOr<TodoItem> TodoManager::GetTodo(const std::string& id) const {
|
||||
auto it = std::find_if(todos_.begin(), todos_.end(),
|
||||
[&id](const TodoItem& t) { return t.id == id; });
|
||||
|
||||
if (it == todos_.end()) {
|
||||
return absl::NotFoundError(absl::StrFormat("TODO with ID %s not found", id));
|
||||
}
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
std::vector<TodoItem> TodoManager::GetAllTodos() const {
|
||||
return todos_;
|
||||
}
|
||||
|
||||
std::vector<TodoItem> TodoManager::GetTodosByStatus(TodoItem::Status status) const {
|
||||
std::vector<TodoItem> result;
|
||||
std::copy_if(todos_.begin(), todos_.end(), std::back_inserter(result),
|
||||
[status](const TodoItem& t) { return t.status == status; });
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<TodoItem> TodoManager::GetTodosByCategory(const std::string& category) const {
|
||||
std::vector<TodoItem> result;
|
||||
std::copy_if(todos_.begin(), todos_.end(), std::back_inserter(result),
|
||||
[&category](const TodoItem& t) { return t.category == category; });
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TodoManager::CanExecute(const TodoItem& item) const {
|
||||
// Check if all dependencies are completed
|
||||
for (const auto& dep_id : item.dependencies) {
|
||||
auto dep_result = GetTodo(dep_id);
|
||||
if (!dep_result.ok()) {
|
||||
return false; // Dependency not found
|
||||
}
|
||||
if (dep_result->status != TodoItem::Status::COMPLETED) {
|
||||
return false; // Dependency not completed
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
absl::StatusOr<TodoItem> TodoManager::GetNextActionableTodo() const {
|
||||
// Find pending/blocked TODOs
|
||||
std::vector<TodoItem> actionable;
|
||||
for (const auto& item : todos_) {
|
||||
if ((item.status == TodoItem::Status::PENDING ||
|
||||
item.status == TodoItem::Status::BLOCKED) &&
|
||||
CanExecute(item)) {
|
||||
actionable.push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (actionable.empty()) {
|
||||
return absl::NotFoundError("No actionable TODOs found");
|
||||
}
|
||||
|
||||
// Sort by priority (descending)
|
||||
std::sort(actionable.begin(), actionable.end(),
|
||||
[](const TodoItem& a, const TodoItem& b) {
|
||||
return a.priority > b.priority;
|
||||
});
|
||||
|
||||
return actionable[0];
|
||||
}
|
||||
|
||||
absl::Status TodoManager::DeleteTodo(const std::string& id) {
|
||||
auto it = std::find_if(todos_.begin(), todos_.end(),
|
||||
[&id](const TodoItem& t) { return t.id == id; });
|
||||
|
||||
if (it == todos_.end()) {
|
||||
return absl::NotFoundError(absl::StrFormat("TODO with ID %s not found", id));
|
||||
}
|
||||
|
||||
todos_.erase(it);
|
||||
return Save();
|
||||
}
|
||||
|
||||
absl::Status TodoManager::ClearCompleted() {
|
||||
auto it = std::remove_if(todos_.begin(), todos_.end(),
|
||||
[](const TodoItem& t) {
|
||||
return t.status == TodoItem::Status::COMPLETED;
|
||||
});
|
||||
todos_.erase(it, todos_.end());
|
||||
return Save();
|
||||
}
|
||||
|
||||
absl::StatusOr<std::vector<TodoItem>> TodoManager::GenerateExecutionPlan() const {
|
||||
std::vector<TodoItem> plan;
|
||||
std::vector<TodoItem> pending;
|
||||
|
||||
// Get all pending TODOs
|
||||
std::copy_if(todos_.begin(), todos_.end(), std::back_inserter(pending),
|
||||
[](const TodoItem& t) {
|
||||
return t.status == TodoItem::Status::PENDING ||
|
||||
t.status == TodoItem::Status::BLOCKED;
|
||||
});
|
||||
|
||||
// Topological sort based on dependencies
|
||||
std::vector<TodoItem> sorted;
|
||||
std::set<std::string> completed_ids;
|
||||
|
||||
while (!pending.empty()) {
|
||||
bool made_progress = false;
|
||||
|
||||
for (auto it = pending.begin(); it != pending.end(); ) {
|
||||
bool can_add = true;
|
||||
for (const auto& dep_id : it->dependencies) {
|
||||
if (completed_ids.find(dep_id) == completed_ids.end()) {
|
||||
can_add = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (can_add) {
|
||||
sorted.push_back(*it);
|
||||
completed_ids.insert(it->id);
|
||||
it = pending.erase(it);
|
||||
made_progress = true;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (!made_progress && !pending.empty()) {
|
||||
return absl::FailedPreconditionError(
|
||||
"Circular dependency detected in TODOs");
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by priority within dependency levels
|
||||
std::stable_sort(sorted.begin(), sorted.end(),
|
||||
[](const TodoItem& a, const TodoItem& b) {
|
||||
return a.priority > b.priority;
|
||||
});
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
absl::Status TodoManager::Save() {
|
||||
#ifdef YAZE_WITH_JSON
|
||||
json j_todos = json::array();
|
||||
|
||||
for (const auto& item : todos_) {
|
||||
json j_item;
|
||||
j_item["id"] = item.id;
|
||||
j_item["description"] = item.description;
|
||||
j_item["status"] = item.StatusToString();
|
||||
j_item["category"] = item.category;
|
||||
j_item["priority"] = item.priority;
|
||||
j_item["dependencies"] = item.dependencies;
|
||||
j_item["tools_needed"] = item.tools_needed;
|
||||
j_item["created_at"] = item.created_at;
|
||||
j_item["updated_at"] = item.updated_at;
|
||||
j_item["notes"] = item.notes;
|
||||
|
||||
j_todos.push_back(j_item);
|
||||
}
|
||||
|
||||
std::ofstream file(todos_file_);
|
||||
if (!file.is_open()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to open file: %s", todos_file_));
|
||||
}
|
||||
|
||||
file << j_todos.dump(2);
|
||||
return absl::OkStatus();
|
||||
#else
|
||||
return absl::UnimplementedError("JSON support required for TODO persistence");
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::Status TodoManager::Load() {
|
||||
#ifdef YAZE_WITH_JSON
|
||||
std::ifstream file(todos_file_);
|
||||
if (!file.is_open()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to open file: %s", todos_file_));
|
||||
}
|
||||
|
||||
json j_todos;
|
||||
try {
|
||||
file >> j_todos;
|
||||
} catch (const std::exception& e) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to parse JSON: %s", e.what()));
|
||||
}
|
||||
|
||||
todos_.clear();
|
||||
for (const auto& j_item : j_todos) {
|
||||
TodoItem item;
|
||||
item.id = j_item.value("id", "");
|
||||
item.description = j_item.value("description", "");
|
||||
item.status = TodoItem::StringToStatus(j_item.value("status", "pending"));
|
||||
item.category = j_item.value("category", "");
|
||||
item.priority = j_item.value("priority", 0);
|
||||
item.dependencies = j_item.value("dependencies", std::vector<std::string>{});
|
||||
item.tools_needed = j_item.value("tools_needed", std::vector<std::string>{});
|
||||
item.created_at = j_item.value("created_at", "");
|
||||
item.updated_at = j_item.value("updated_at", "");
|
||||
item.notes = j_item.value("notes", "");
|
||||
|
||||
todos_.push_back(item);
|
||||
|
||||
// Update next_id_
|
||||
if (item.id.find("todo_") == 0) {
|
||||
int id_num = std::stoi(item.id.substr(5));
|
||||
if (id_num >= next_id_) {
|
||||
next_id_ = id_num + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
#else
|
||||
return absl::UnimplementedError("JSON support required for TODO persistence");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string TodoManager::ExportAsJson() const {
|
||||
#ifdef YAZE_WITH_JSON
|
||||
json j_todos = json::array();
|
||||
|
||||
for (const auto& item : todos_) {
|
||||
json j_item;
|
||||
j_item["id"] = item.id;
|
||||
j_item["description"] = item.description;
|
||||
j_item["status"] = item.StatusToString();
|
||||
j_item["category"] = item.category;
|
||||
j_item["priority"] = item.priority;
|
||||
j_item["dependencies"] = item.dependencies;
|
||||
j_item["tools_needed"] = item.tools_needed;
|
||||
j_item["created_at"] = item.created_at;
|
||||
j_item["updated_at"] = item.updated_at;
|
||||
j_item["notes"] = item.notes;
|
||||
|
||||
j_todos.push_back(j_item);
|
||||
}
|
||||
|
||||
return j_todos.dump(2);
|
||||
#else
|
||||
return "{}";
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::Status TodoManager::ImportFromJson(const std::string& json_str) {
|
||||
#ifdef YAZE_WITH_JSON
|
||||
try {
|
||||
json j_todos = json::parse(json_str);
|
||||
|
||||
todos_.clear();
|
||||
for (const auto& j_item : j_todos) {
|
||||
TodoItem item;
|
||||
item.id = j_item.value("id", "");
|
||||
item.description = j_item.value("description", "");
|
||||
item.status = TodoItem::StringToStatus(j_item.value("status", "pending"));
|
||||
item.category = j_item.value("category", "");
|
||||
item.priority = j_item.value("priority", 0);
|
||||
item.dependencies = j_item.value("dependencies", std::vector<std::string>{});
|
||||
item.tools_needed = j_item.value("tools_needed", std::vector<std::string>{});
|
||||
item.created_at = j_item.value("created_at", "");
|
||||
item.updated_at = j_item.value("updated_at", "");
|
||||
item.notes = j_item.value("notes", "");
|
||||
|
||||
todos_.push_back(item);
|
||||
}
|
||||
|
||||
return Save();
|
||||
} catch (const std::exception& e) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to parse JSON: %s", e.what()));
|
||||
}
|
||||
#else
|
||||
return absl::UnimplementedError("JSON support required for TODO import");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace agent
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
156
src/cli/service/agent/todo_manager.h
Normal file
156
src/cli/service/agent/todo_manager.h
Normal file
@@ -0,0 +1,156 @@
|
||||
#ifndef YAZE_CLI_SERVICE_AGENT_TODO_MANAGER_H_
|
||||
#define YAZE_CLI_SERVICE_AGENT_TODO_MANAGER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace agent {
|
||||
|
||||
/**
|
||||
* @struct TodoItem
|
||||
* @brief Represents a single TODO item for task management
|
||||
*/
|
||||
struct TodoItem {
|
||||
std::string id;
|
||||
std::string description;
|
||||
enum class Status {
|
||||
PENDING,
|
||||
IN_PROGRESS,
|
||||
COMPLETED,
|
||||
BLOCKED,
|
||||
CANCELLED
|
||||
} status = Status::PENDING;
|
||||
|
||||
std::string category; // e.g., "rom_edit", "ai_task", "build", "test"
|
||||
int priority = 0; // Higher = more important
|
||||
std::vector<std::string> dependencies; // IDs of tasks that must complete first
|
||||
std::vector<std::string> tools_needed; // Tools/functions required
|
||||
std::string created_at;
|
||||
std::string updated_at;
|
||||
std::string notes;
|
||||
|
||||
// Convert status enum to string
|
||||
std::string StatusToString() const;
|
||||
static Status StringToStatus(const std::string& str);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class TodoManager
|
||||
* @brief Manages TODO lists for z3ed agent task execution
|
||||
*
|
||||
* Enables the AI agent to:
|
||||
* - Create and track TODO lists for complex tasks
|
||||
* - Break down goals into executable steps
|
||||
* - Track dependencies between tasks
|
||||
* - Persist state between sessions
|
||||
* - Generate execution plans
|
||||
*/
|
||||
class TodoManager {
|
||||
public:
|
||||
TodoManager();
|
||||
explicit TodoManager(const std::string& data_dir);
|
||||
|
||||
/**
|
||||
* @brief Initialize the TODO manager and load persisted data
|
||||
*/
|
||||
absl::Status Initialize();
|
||||
|
||||
/**
|
||||
* @brief Create a new TODO item
|
||||
*/
|
||||
absl::StatusOr<TodoItem> CreateTodo(
|
||||
const std::string& description,
|
||||
const std::string& category = "",
|
||||
int priority = 0);
|
||||
|
||||
/**
|
||||
* @brief Update an existing TODO item
|
||||
*/
|
||||
absl::Status UpdateTodo(const std::string& id, const TodoItem& item);
|
||||
|
||||
/**
|
||||
* @brief Update TODO status
|
||||
*/
|
||||
absl::Status UpdateStatus(const std::string& id, TodoItem::Status status);
|
||||
|
||||
/**
|
||||
* @brief Get a TODO item by ID
|
||||
*/
|
||||
absl::StatusOr<TodoItem> GetTodo(const std::string& id) const;
|
||||
|
||||
/**
|
||||
* @brief Get all TODO items
|
||||
*/
|
||||
std::vector<TodoItem> GetAllTodos() const;
|
||||
|
||||
/**
|
||||
* @brief Get TODO items by status
|
||||
*/
|
||||
std::vector<TodoItem> GetTodosByStatus(TodoItem::Status status) const;
|
||||
|
||||
/**
|
||||
* @brief Get TODO items by category
|
||||
*/
|
||||
std::vector<TodoItem> GetTodosByCategory(const std::string& category) const;
|
||||
|
||||
/**
|
||||
* @brief Get the next actionable TODO (respecting dependencies and priority)
|
||||
*/
|
||||
absl::StatusOr<TodoItem> GetNextActionableTodo() const;
|
||||
|
||||
/**
|
||||
* @brief Delete a TODO item
|
||||
*/
|
||||
absl::Status DeleteTodo(const std::string& id);
|
||||
|
||||
/**
|
||||
* @brief Clear all completed TODOs
|
||||
*/
|
||||
absl::Status ClearCompleted();
|
||||
|
||||
/**
|
||||
* @brief Save TODOs to persistent storage
|
||||
*/
|
||||
absl::Status Save();
|
||||
|
||||
/**
|
||||
* @brief Load TODOs from persistent storage
|
||||
*/
|
||||
absl::Status Load();
|
||||
|
||||
/**
|
||||
* @brief Generate an execution plan based on dependencies
|
||||
*/
|
||||
absl::StatusOr<std::vector<TodoItem>> GenerateExecutionPlan() const;
|
||||
|
||||
/**
|
||||
* @brief Export TODOs as JSON string
|
||||
*/
|
||||
std::string ExportAsJson() const;
|
||||
|
||||
/**
|
||||
* @brief Import TODOs from JSON string
|
||||
*/
|
||||
absl::Status ImportFromJson(const std::string& json);
|
||||
|
||||
private:
|
||||
std::string data_dir_;
|
||||
std::string todos_file_;
|
||||
std::vector<TodoItem> todos_;
|
||||
int next_id_ = 1;
|
||||
|
||||
std::string GenerateId();
|
||||
std::string GetTimestamp() const;
|
||||
bool CanExecute(const TodoItem& item) const;
|
||||
};
|
||||
|
||||
} // namespace agent
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_CLI_SERVICE_AGENT_TODO_MANAGER_H_
|
||||
@@ -238,63 +238,20 @@ absl::StatusOr<std::filesystem::path> AIGUIController::CaptureCurrentState(
|
||||
|
||||
absl::Status AIGUIController::ExecuteGRPCAction(const AIAction& action) {
|
||||
// Convert AI action to gRPC test commands
|
||||
auto grpc_commands = action_generator_.GenerateGRPCCommands({action});
|
||||
auto test_script_result = action_generator_.GenerateTestScript({action});
|
||||
|
||||
if (grpc_commands.empty()) {
|
||||
return absl::InternalError("No gRPC commands generated for action");
|
||||
if (!test_script_result.ok()) {
|
||||
return test_script_result.status();
|
||||
}
|
||||
|
||||
// Execute each command
|
||||
for (const auto& command_json : grpc_commands) {
|
||||
// Parse JSON and execute via GUI client
|
||||
// This is a placeholder - actual implementation would parse JSON
|
||||
// and call appropriate GUI client methods
|
||||
|
||||
if (action.type == AIActionType::kClickButton) {
|
||||
auto button_it = action.parameters.find("button");
|
||||
if (button_it != action.parameters.end()) {
|
||||
auto status = gui_client_->ClickButton(button_it->second);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action.type == AIActionType::kPlaceTile) {
|
||||
// Extract parameters
|
||||
auto x_it = action.parameters.find("x");
|
||||
auto y_it = action.parameters.find("y");
|
||||
auto tile_it = action.parameters.find("tile_id");
|
||||
|
||||
if (x_it != action.parameters.end() &&
|
||||
y_it != action.parameters.end() &&
|
||||
tile_it != action.parameters.end()) {
|
||||
|
||||
int x = std::stoi(x_it->second);
|
||||
int y = std::stoi(y_it->second);
|
||||
int tile_id = std::stoi(tile_it->second);
|
||||
|
||||
// Use GUI client to place tile
|
||||
// (This would need actual implementation in GuiAutomationClient)
|
||||
auto status = gui_client_->ExecuteTestScript(
|
||||
absl::StrFormat("PlaceTile(%d, %d, %d)", x, y, tile_id));
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action.type == AIActionType::kWait) {
|
||||
int wait_ms = config_.screenshot_delay_ms;
|
||||
auto wait_it = action.parameters.find("duration_ms");
|
||||
if (wait_it != action.parameters.end()) {
|
||||
wait_ms = std::stoi(wait_it->second);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms));
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
// TODO: Implement gRPC GUI automation when GuiAutomationClient is ready
|
||||
// For now, just log the generated test script
|
||||
return absl::UnimplementedError(
|
||||
"gRPC GUI automation not yet fully implemented. "
|
||||
"GuiAutomationClient integration pending.");
|
||||
}
|
||||
|
||||
|
||||
absl::StatusOr<VisionAnalysisResult> AIGUIController::VerifyActionSuccess(
|
||||
const AIAction& action,
|
||||
const std::filesystem::path& before_screenshot,
|
||||
|
||||
Reference in New Issue
Block a user