a76ec74338
C++ ImGui kanban for steering LLM agents. Six panels (Board, Calendar, Dashboard, Agent runs, Worktrees, DoD inspector) wired to registry functions http_request, kpi_card, sparkline, agent_runs_timeline, dod_evidence_panel. Backend Go on :8403 (independent operations.db from kanban_web).
92 lines
2.6 KiB
C++
92 lines
2.6 KiB
C++
// panel_worktrees.cpp — lists git worktrees via `git worktree list --porcelain`.
|
|
//
|
|
// Read-only MVP: shows path, head, branch. Future work: create/remove from
|
|
// inside the panel (TODO in app.md ## Gotchas).
|
|
#include "panels.h"
|
|
#include "core/icons_tabler.h"
|
|
|
|
#include <imgui.h>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <array>
|
|
|
|
namespace kanban_cpp {
|
|
|
|
namespace {
|
|
|
|
struct WT {
|
|
std::string path;
|
|
std::string head;
|
|
std::string branch;
|
|
};
|
|
|
|
std::vector<WT> scan_worktrees() {
|
|
std::vector<WT> out;
|
|
#ifdef _WIN32
|
|
FILE* fp = _popen("git worktree list --porcelain 2>nul", "r");
|
|
#else
|
|
FILE* fp = popen("git worktree list --porcelain 2>/dev/null", "r");
|
|
#endif
|
|
if (!fp) return out;
|
|
std::array<char, 1024> buf;
|
|
WT cur;
|
|
while (std::fgets(buf.data(), static_cast<int>(buf.size()), fp)) {
|
|
std::string line(buf.data());
|
|
while (!line.empty() && (line.back() == '\n' || line.back() == '\r')) line.pop_back();
|
|
if (line.empty()) {
|
|
if (!cur.path.empty()) out.push_back(cur);
|
|
cur = WT();
|
|
continue;
|
|
}
|
|
if (line.rfind("worktree ", 0) == 0) cur.path = line.substr(9);
|
|
else if (line.rfind("HEAD ", 0) == 0) cur.head = line.substr(5);
|
|
else if (line.rfind("branch ", 0) == 0) cur.branch = line.substr(7);
|
|
}
|
|
if (!cur.path.empty()) out.push_back(cur);
|
|
#ifdef _WIN32
|
|
_pclose(fp);
|
|
#else
|
|
pclose(fp);
|
|
#endif
|
|
return out;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void draw_worktrees(AppState& /*s*/, bool* p_open) {
|
|
if (!ImGui::Begin(TI_GIT_BRANCH " Worktrees", p_open)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
static std::vector<WT> wts;
|
|
static bool first = true;
|
|
if (first) { wts = scan_worktrees(); first = false; }
|
|
if (ImGui::Button(TI_REFRESH " Rescan")) wts = scan_worktrees();
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("%zu worktrees", wts.size());
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::BeginTable("##wts", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
|
ImGui::TableSetupColumn("Branch");
|
|
ImGui::TableSetupColumn("HEAD");
|
|
ImGui::TableSetupColumn("Path");
|
|
ImGui::TableHeadersRow();
|
|
for (const auto& w : wts) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::TextUnformatted(w.branch.empty() ? "(detached)" : w.branch.c_str());
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGui::TextUnformatted(w.head.substr(0, 10).c_str());
|
|
ImGui::TableSetColumnIndex(2);
|
|
ImGui::TextUnformatted(w.path.c_str());
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
} // namespace kanban_cpp
|