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:
scawful
2025-10-05 00:19:57 -04:00
parent 6387352ecc
commit eec9f84fb0
8 changed files with 932 additions and 55 deletions

View File

@@ -83,9 +83,9 @@ if(YAZE_WITH_GRPC)
# Add proto definitions for test harness and ROM service
target_add_protobuf(yaze_core_lib
${CMAKE_SOURCE_DIR}/src/app/core/proto/imgui_test_harness.proto)
${PROJECT_SOURCE_DIR}/src/app/core/proto/imgui_test_harness.proto)
target_add_protobuf(yaze_core_lib
${CMAKE_SOURCE_DIR}/protos/rom_service.proto)
${PROJECT_SOURCE_DIR}/protos/rom_service.proto)
# Add test harness implementation
target_sources(yaze_core_lib PRIVATE

View File

@@ -67,7 +67,7 @@ endif()
# Add gRPC support for ROM service
if(YAZE_WITH_GRPC)
target_add_protobuf(yaze_net ${CMAKE_SOURCE_DIR}/protos/rom_service.proto)
target_add_protobuf(yaze_net ${PROJECT_SOURCE_DIR}/protos/rom_service.proto)
target_link_libraries(yaze_net PUBLIC
grpc++

View File

@@ -67,10 +67,12 @@ _yaze_ensure_yaml_cpp(YAZE_YAML_CPP_TARGET)
set(YAZE_AGENT_SOURCES
cli/service/agent/proposal_executor.cc
cli/handlers/agent/tool_commands.cc
cli/handlers/agent/todo_commands.cc
cli/service/agent/conversational_agent_service.cc
cli/service/agent/simple_chat_session.cc
cli/service/agent/tool_dispatcher.cc
cli/service/agent/learned_knowledge_service.cc
cli/service/agent/todo_manager.cc
cli/service/ai/ai_service.cc
cli/service/ai/ai_action_parser.cc
cli/service/ai/vision_action_refiner.cc
@@ -79,6 +81,7 @@ set(YAZE_AGENT_SOURCES
cli/service/ai/prompt_builder.cc
cli/service/ai/service_factory.cc
cli/service/gui/gui_action_generator.cc
cli/service/gui/gui_automation_client.cc
cli/service/net/z3ed_network_client.cc
cli/handlers/net/net_commands.cc
cli/service/planning/policy_evaluator.cc
@@ -149,6 +152,10 @@ endif()
# Add gRPC support for GUI automation
if(YAZE_WITH_GRPC)
# Generate proto files for yaze_agent
target_add_protobuf(yaze_agent
${PROJECT_SOURCE_DIR}/src/app/core/proto/imgui_test_harness.proto)
target_link_libraries(yaze_agent PUBLIC
grpc++
grpc++_reflection

View File

@@ -0,0 +1,302 @@
#include "cli/handlers/agent/todo_commands.h"
#include <iostream>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "cli/service/agent/todo_manager.h"
namespace yaze {
namespace cli {
namespace handlers {
namespace {
using agent::TodoItem;
using agent::TodoManager;
// Global TODO manager instance
TodoManager& GetTodoManager() {
static TodoManager manager;
static bool initialized = false;
if (!initialized) {
auto status = manager.Initialize();
if (!status.ok()) {
std::cerr << "Warning: Failed to initialize TODO manager: "
<< status.message() << std::endl;
}
initialized = true;
}
return manager;
}
void PrintTodo(const TodoItem& item, bool detailed = false) {
std::string status_emoji;
switch (item.status) {
case TodoItem::Status::PENDING: status_emoji = ""; break;
case TodoItem::Status::IN_PROGRESS: status_emoji = "🔄"; break;
case TodoItem::Status::COMPLETED: status_emoji = ""; break;
case TodoItem::Status::BLOCKED: status_emoji = "🚫"; break;
case TodoItem::Status::CANCELLED: status_emoji = ""; break;
}
std::cout << absl::StreamFormat("[%s] %s %s",
item.id,
status_emoji,
item.description);
if (!item.category.empty()) {
std::cout << absl::StreamFormat(" [%s]", item.category);
}
if (item.priority > 0) {
std::cout << absl::StreamFormat(" (priority: %d)", item.priority);
}
std::cout << std::endl;
if (detailed) {
std::cout << " Status: " << item.StatusToString() << std::endl;
std::cout << " Created: " << item.created_at << std::endl;
std::cout << " Updated: " << item.updated_at << std::endl;
if (!item.dependencies.empty()) {
std::cout << " Dependencies: " << absl::StrJoin(item.dependencies, ", ") << std::endl;
}
if (!item.tools_needed.empty()) {
std::cout << " Tools needed: " << absl::StrJoin(item.tools_needed, ", ") << std::endl;
}
if (!item.notes.empty()) {
std::cout << " Notes: " << item.notes << std::endl;
}
}
}
absl::Status HandleTodoCreate(const std::vector<std::string>& args) {
if (args.empty()) {
return absl::InvalidArgumentError("Usage: agent todo create <description> [--category=<cat>] [--priority=<n>]");
}
std::string description = args[0];
std::string category;
int priority = 0;
for (size_t i = 1; i < args.size(); ++i) {
if (args[i].find("--category=") == 0) {
category = args[i].substr(11);
} else if (args[i].find("--priority=") == 0) {
priority = std::stoi(args[i].substr(11));
}
}
auto& manager = GetTodoManager();
auto result = manager.CreateTodo(description, category, priority);
if (!result.ok()) {
return result.status();
}
std::cout << "Created TODO:" << std::endl;
PrintTodo(*result, true);
return absl::OkStatus();
}
absl::Status HandleTodoList(const std::vector<std::string>& args) {
std::string status_filter;
std::string category_filter;
for (const auto& arg : args) {
if (arg.find("--status=") == 0) {
status_filter = arg.substr(9);
} else if (arg.find("--category=") == 0) {
category_filter = arg.substr(11);
}
}
auto& manager = GetTodoManager();
std::vector<TodoItem> todos;
if (!status_filter.empty()) {
auto status = TodoItem::StringToStatus(status_filter);
todos = manager.GetTodosByStatus(status);
} else if (!category_filter.empty()) {
todos = manager.GetTodosByCategory(category_filter);
} else {
todos = manager.GetAllTodos();
}
if (todos.empty()) {
std::cout << "No TODOs found." << std::endl;
return absl::OkStatus();
}
std::cout << "TODOs (" << todos.size() << "):" << std::endl;
for (const auto& item : todos) {
PrintTodo(item);
}
return absl::OkStatus();
}
absl::Status HandleTodoUpdate(const std::vector<std::string>& args) {
if (args.size() < 2) {
return absl::InvalidArgumentError("Usage: agent todo update <id> --status=<status>");
}
std::string id = args[0];
std::string new_status_str;
for (size_t i = 1; i < args.size(); ++i) {
if (args[i].find("--status=") == 0) {
new_status_str = args[i].substr(9);
}
}
if (new_status_str.empty()) {
return absl::InvalidArgumentError("--status parameter is required");
}
auto new_status = TodoItem::StringToStatus(new_status_str);
auto& manager = GetTodoManager();
auto status = manager.UpdateStatus(id, new_status);
if (!status.ok()) {
return status;
}
std::cout << "Updated TODO " << id << " to status: " << new_status_str << std::endl;
return absl::OkStatus();
}
absl::Status HandleTodoShow(const std::vector<std::string>& args) {
if (args.empty()) {
return absl::InvalidArgumentError("Usage: agent todo show <id>");
}
std::string id = args[0];
auto& manager = GetTodoManager();
auto result = manager.GetTodo(id);
if (!result.ok()) {
return result.status();
}
PrintTodo(*result, true);
return absl::OkStatus();
}
absl::Status HandleTodoDelete(const std::vector<std::string>& args) {
if (args.empty()) {
return absl::InvalidArgumentError("Usage: agent todo delete <id>");
}
std::string id = args[0];
auto& manager = GetTodoManager();
auto status = manager.DeleteTodo(id);
if (!status.ok()) {
return status;
}
std::cout << "Deleted TODO " << id << std::endl;
return absl::OkStatus();
}
absl::Status HandleTodoClearCompleted(const std::vector<std::string>& args) {
auto& manager = GetTodoManager();
auto status = manager.ClearCompleted();
if (!status.ok()) {
return status;
}
std::cout << "Cleared all completed TODOs" << std::endl;
return absl::OkStatus();
}
absl::Status HandleTodoNext(const std::vector<std::string>& args) {
auto& manager = GetTodoManager();
auto result = manager.GetNextActionableTodo();
if (!result.ok()) {
return result.status();
}
std::cout << "Next actionable TODO:" << std::endl;
PrintTodo(*result, true);
return absl::OkStatus();
}
absl::Status HandleTodoPlan(const std::vector<std::string>& args) {
auto& manager = GetTodoManager();
auto result = manager.GenerateExecutionPlan();
if (!result.ok()) {
return result.status();
}
auto& plan = *result;
if (plan.empty()) {
std::cout << "No pending TODOs." << std::endl;
return absl::OkStatus();
}
std::cout << "Execution Plan (" << plan.size() << " tasks):" << std::endl;
for (size_t i = 0; i < plan.size(); ++i) {
std::cout << absl::StreamFormat("%2d. ", i + 1);
PrintTodo(plan[i]);
}
return absl::OkStatus();
}
} // namespace
absl::Status HandleTodoCommand(const std::vector<std::string>& args) {
if (args.empty()) {
std::cerr << "Usage: agent todo <command> [options]" << std::endl;
std::cerr << "Commands:" << std::endl;
std::cerr << " create - Create a new TODO" << std::endl;
std::cerr << " list - List all TODOs" << std::endl;
std::cerr << " update - Update TODO status" << std::endl;
std::cerr << " show - Show TODO details" << std::endl;
std::cerr << " delete - Delete a TODO" << std::endl;
std::cerr << " clear-completed - Clear completed TODOs" << std::endl;
std::cerr << " next - Get next actionable TODO" << std::endl;
std::cerr << " plan - Generate execution plan" << std::endl;
return absl::InvalidArgumentError("No command specified");
}
std::string subcommand = args[0];
std::vector<std::string> subargs(args.begin() + 1, args.end());
if (subcommand == "create") {
return HandleTodoCreate(subargs);
} else if (subcommand == "list") {
return HandleTodoList(subargs);
} else if (subcommand == "update") {
return HandleTodoUpdate(subargs);
} else if (subcommand == "show") {
return HandleTodoShow(subargs);
} else if (subcommand == "delete") {
return HandleTodoDelete(subargs);
} else if (subcommand == "clear-completed") {
return HandleTodoClearCompleted(subargs);
} else if (subcommand == "next") {
return HandleTodoNext(subargs);
} else if (subcommand == "plan") {
return HandleTodoPlan(subargs);
} else {
return absl::InvalidArgumentError(
absl::StrFormat("Unknown todo command: %s", subcommand));
}
}
} // namespace handlers
} // namespace cli
} // namespace yaze

View File

@@ -0,0 +1,32 @@
#ifndef YAZE_CLI_HANDLERS_AGENT_TODO_COMMANDS_H_
#define YAZE_CLI_HANDLERS_AGENT_TODO_COMMANDS_H_
#include <string>
#include <vector>
#include "absl/status/status.h"
namespace yaze {
namespace cli {
namespace handlers {
/**
* @brief Handle z3ed agent todo commands
*
* Commands:
* agent todo create <description> [--category=<cat>] [--priority=<n>]
* agent todo list [--status=<status>] [--category=<cat>]
* agent todo update <id> --status=<status>
* agent todo show <id>
* agent todo delete <id>
* agent todo clear-completed
* agent todo next
* agent todo plan
*/
absl::Status HandleTodoCommand(const std::vector<std::string>& args);
} // namespace handlers
} // namespace cli
} // namespace yaze
#endif // YAZE_CLI_HANDLERS_AGENT_TODO_COMMANDS_H_

View 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

View 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_

View File

@@ -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,