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.
82 lines
2.1 KiB
C++
82 lines
2.1 KiB
C++
#pragma once
|
|
|
|
#include "picojson.h"
|
|
|
|
#include <condition_variable>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
namespace tp::taskboard {
|
|
|
|
struct McpServerCommand {
|
|
std::string executable;
|
|
std::vector<std::string> arguments;
|
|
std::vector<std::pair<std::string, std::string>> environment;
|
|
std::string stderrPath;
|
|
};
|
|
|
|
struct McpToolResult {
|
|
bool ok = false;
|
|
picojson::value value;
|
|
std::string error;
|
|
};
|
|
|
|
class McpClient {
|
|
public:
|
|
using Completion = std::function<void(const McpToolResult&)>;
|
|
|
|
McpClient();
|
|
~McpClient();
|
|
|
|
McpClient(const McpClient&) = delete;
|
|
McpClient& operator=(const McpClient&) = delete;
|
|
|
|
bool start(const McpServerCommand& command, std::string& error);
|
|
void stop();
|
|
[[nodiscard]] bool isRunning() const;
|
|
|
|
void callTool(const std::string& name, picojson::object arguments, Completion completion);
|
|
McpToolResult callToolBlocking(const std::string& name, picojson::object arguments);
|
|
|
|
void dispatchCompletions();
|
|
[[nodiscard]] size_t pendingCount() const;
|
|
void waitForIdle();
|
|
void setOnResponse(std::function<void()> callback);
|
|
|
|
private:
|
|
struct Pending {
|
|
Completion completion;
|
|
};
|
|
|
|
bool spawn(const McpServerCommand& command, std::string& error);
|
|
bool handshake(std::string& error);
|
|
void readLoop();
|
|
void handleMessage(const std::string& line);
|
|
long nextId();
|
|
bool send(const picojson::object& message);
|
|
bool request(const std::string& method, picojson::object params, Completion completion);
|
|
bool waitUntil(const std::function<bool()>& finished);
|
|
void failAll(const std::string& reason);
|
|
void deliver(Completion completion, McpToolResult result);
|
|
|
|
[[nodiscard]] static McpToolResult toolResult(const picojson::value& response);
|
|
|
|
int mPid = -1;
|
|
int mToServer = -1;
|
|
int mFromServer = -1;
|
|
std::thread mReader;
|
|
|
|
mutable std::mutex mMutex;
|
|
std::condition_variable mResponded;
|
|
std::map<long, Pending> mPending;
|
|
std::deque<std::pair<Completion, McpToolResult>> mReady;
|
|
long mNextId = 1;
|
|
bool mRunning = false;
|
|
std::function<void()> mOnResponse;
|
|
};
|
|
}
|