The board edits the agent task database through the Python MCP server, which now lives in this module (Taskboard/server). Code is versioned here; the data (tasks.db and per-task session workspaces) stays in AGENTS_DATA_DIR, by default ~/agents/manager, decided only in server/paths.py. UI: - Tasks are graph nodes; parent/child and dependency links map to set_parent and dependencies. Dropping a link on empty space creates a connected task. - Inspector edits title, description, status, priority, model, effort, permission mode and colour. - Edits stay local until Save (Ctrl+S, File > Save); the status bar shows the unsaved count. Unsaved work is the difference between the board and the last server board (BoardSnapshot), saved atomically with save_board. - Server changes merge in on a one-second poll without touching unsaved edits; the same field changed on both sides becomes a keep-mine/use-theirs conflict. - Sessions: start, stop, restart with new settings (keeping or dropping the conversation), open in a terminal, send messages; a node badge shows the live session state. Server: - Headless Claude Code sessions, one tmux session per task, with state from hooks and the exit reason recorded; manual or auto permission modes. - save_board, edit_task conflict checks, field history, get_board and revision polling, session tools, task workspaces with report conventions.
73 lines
1.8 KiB
C++
73 lines
1.8 KiB
C++
#pragma once
|
|
|
|
#include "TaskBoard.hpp"
|
|
#include "TaskStore.hpp"
|
|
|
|
#include "picojson.h"
|
|
|
|
#include <map>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace tp::taskboard {
|
|
|
|
enum class LinkKind { Parent, Dependency };
|
|
|
|
struct TaskLink {
|
|
LinkKind kind = LinkKind::Parent;
|
|
ui::NodeId from = 0;
|
|
ui::NodeId to = 0;
|
|
|
|
auto operator<=>(const TaskLink&) const = default;
|
|
};
|
|
|
|
struct NodeLayout {
|
|
Vec2F position;
|
|
halnf width = 0;
|
|
halnf bodyHeight = 0;
|
|
bool collapsed = false;
|
|
|
|
bool operator==(const NodeLayout&) const = default;
|
|
};
|
|
|
|
struct TaskRecord {
|
|
std::map<TaskField, picojson::value> fields;
|
|
NodeLayout layout;
|
|
};
|
|
|
|
// A complete description of the board's persistent content. The server's last known board and the
|
|
// board the user is editing are both snapshots; what is unsaved is exactly their difference.
|
|
struct BoardSnapshot {
|
|
std::map<ui::NodeId, TaskRecord> tasks;
|
|
std::set<TaskLink> links;
|
|
std::vector<std::string> palette;
|
|
|
|
[[nodiscard]] static BoardSnapshot capture(const TaskBoard& board);
|
|
};
|
|
|
|
struct FieldEdit {
|
|
ui::NodeId task = 0;
|
|
TaskField field = TaskField::Title;
|
|
picojson::value value;
|
|
picojson::value expected;
|
|
};
|
|
|
|
struct BoardChanges {
|
|
std::vector<ui::NodeId> created;
|
|
std::vector<ui::NodeId> deleted;
|
|
std::vector<FieldEdit> edits;
|
|
std::vector<ui::NodeId> layouts;
|
|
std::vector<TaskLink> linksAdded;
|
|
std::vector<TaskLink> linksRemoved;
|
|
std::optional<std::vector<std::string>> palette;
|
|
|
|
[[nodiscard]] bool empty() const;
|
|
[[nodiscard]] size_t count() const;
|
|
[[nodiscard]] static BoardChanges between(const BoardSnapshot& saved, const BoardSnapshot& current);
|
|
};
|
|
|
|
[[nodiscard]] LinkKind linkKind(const ui::Link& link);
|
|
[[nodiscard]] const std::vector<TaskField>& persistentFields();
|
|
}
|