feat(beads): integrate Beads Mode backend, MCP tools, and GUI support
This commit is contained in:
+97
-36
@@ -1931,6 +1931,13 @@ class App:
|
||||
proj_name = self.project.get("project", {}).get("name", Path(self.active_project_path).stem)
|
||||
imgui.text_colored(C_IN, f"Active: {proj_name}")
|
||||
imgui.separator()
|
||||
imgui.text("Execution Mode")
|
||||
modes = ["native", "beads"]
|
||||
current_idx = modes.index(self.ui_project_execution_mode) if self.ui_project_execution_mode in modes else 0
|
||||
ch, new_idx = imgui.combo("##exec_mode", current_idx, modes)
|
||||
if ch:
|
||||
self.ui_project_execution_mode = modes[new_idx]
|
||||
imgui.separator()
|
||||
imgui.text("Git Directory")
|
||||
ch, self.ui_project_git_dir = imgui.input_text("##git_dir", self.ui_project_git_dir)
|
||||
imgui.same_line()
|
||||
@@ -4066,47 +4073,53 @@ def hello():
|
||||
return
|
||||
# Task 5.3: Dense Summary Line
|
||||
track_name = self.active_track.description if self.active_track else "None"
|
||||
if getattr(self, "ui_project_execution_mode", "native") == "beads":
|
||||
track_name = "Beads Graph"
|
||||
track_stats = {"percentage": 0.0, "completed": 0, "total": 0, "in_progress": 0, "blocked": 0, "todo": 0}
|
||||
if self.active_track:
|
||||
track_stats = project_manager.calculate_track_progress(self.active_track.tickets)
|
||||
elif self.active_tickets:
|
||||
track_stats = project_manager.calculate_track_progress(self.active_tickets)
|
||||
|
||||
total_cost = 0.0
|
||||
for usage in self.mma_tier_usage.values():
|
||||
model = usage.get('model', 'unknown')
|
||||
in_t = usage.get('input', 0)
|
||||
out_t = usage.get('output', 0)
|
||||
total_cost += cost_tracker.estimate_cost(model, in_t, out_t)
|
||||
total_cost = 0.0
|
||||
for usage in self.mma_tier_usage.values():
|
||||
model = usage.get('model', 'unknown')
|
||||
in_t = usage.get('input', 0)
|
||||
out_t = usage.get('output', 0)
|
||||
total_cost += cost_tracker.estimate_cost(model, in_t, out_t)
|
||||
|
||||
imgui.text("Track:")
|
||||
imgui.same_line()
|
||||
imgui.text_colored(C_VAL, track_name)
|
||||
imgui.same_line()
|
||||
imgui.text(" | Status:")
|
||||
imgui.same_line()
|
||||
if self.mma_status == "paused":
|
||||
c = imgui.ImVec4(1, 0.5, 0, 1)
|
||||
if is_nerv: c = vec4(255, 152, 48)
|
||||
imgui.text_colored(c, "PIPELINE PAUSED")
|
||||
imgui.text("Track:")
|
||||
imgui.same_line()
|
||||
status_col = imgui.ImVec4(1, 1, 1, 1)
|
||||
if self.mma_status == "idle": status_col = imgui.ImVec4(0.7, 0.7, 0.7, 1)
|
||||
elif self.mma_status == "running": status_col = imgui.ImVec4(1, 1, 0, 1)
|
||||
elif self.mma_status == "done": status_col = imgui.ImVec4(0, 1, 0, 1)
|
||||
elif self.mma_status == "error": status_col = imgui.ImVec4(1, 0, 0, 1)
|
||||
elif self.mma_status == "paused": status_col = imgui.ImVec4(1, 0.5, 0, 1)
|
||||
|
||||
if is_nerv:
|
||||
if self.mma_status == "running": status_col = vec4(80, 255, 80) # DATA_GREEN
|
||||
elif self.mma_status == "error": status_col = vec4(255, 72, 64) # ALERT_RED
|
||||
|
||||
imgui.text_colored(status_col, self.mma_status.upper())
|
||||
imgui.same_line()
|
||||
imgui.text(" | Cost:")
|
||||
imgui.same_line()
|
||||
imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), f"${total_cost:,.4f}")
|
||||
imgui.text_colored(C_VAL, track_name)
|
||||
imgui.same_line()
|
||||
imgui.text(" | Status:")
|
||||
imgui.same_line()
|
||||
if self.mma_status == "paused":
|
||||
c = imgui.ImVec4(1, 0.5, 0, 1)
|
||||
if is_nerv: c = vec4(255, 152, 48)
|
||||
imgui.text_colored(c, "PIPELINE PAUSED")
|
||||
imgui.same_line()
|
||||
status_col = imgui.ImVec4(1, 1, 1, 1)
|
||||
if self.mma_status == "idle": status_col = imgui.ImVec4(0.7, 0.7, 0.7, 1)
|
||||
elif self.mma_status == "running": status_col = imgui.ImVec4(1, 1, 0, 1)
|
||||
elif self.mma_status == "done": status_col = imgui.ImVec4(0, 1, 0, 1)
|
||||
elif self.mma_status == "error": status_col = imgui.ImVec4(1, 0, 0, 1)
|
||||
elif self.mma_status == "paused": status_col = imgui.ImVec4(1, 0.5, 0, 1)
|
||||
|
||||
if is_nerv:
|
||||
if self.mma_status == "running": status_col = vec4(80, 255, 80) # DATA_GREEN
|
||||
elif self.mma_status == "error": status_col = vec4(255, 72, 64) # ALERT_RED
|
||||
|
||||
imgui.text_colored(status_col, self.mma_status.upper())
|
||||
imgui.same_line()
|
||||
imgui.text(" | Cost:")
|
||||
imgui.same_line()
|
||||
imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), f"${total_cost:,.4f}")
|
||||
|
||||
# Progress Bar
|
||||
perc = track_stats["percentage"] / 100.0
|
||||
p_color = imgui.ImVec2(0.0, 1.0) # WAIT WRONG TYPE
|
||||
|
||||
# Progress Bar
|
||||
perc = track_stats["percentage"] / 100.0
|
||||
p_color = imgui.ImVec4(0.0, 1.0, 0.0, 1.0)
|
||||
if track_stats["percentage"] < 33:
|
||||
p_color = imgui.ImVec4(1.0, 0.0, 0.0, 1.0)
|
||||
@@ -4448,12 +4461,16 @@ def hello():
|
||||
else:
|
||||
imgui.text_disabled("Tier 4 stream is detached.")
|
||||
imgui.end_tab_item()
|
||||
if getattr(self, "ui_project_execution_mode", "native") == "beads":
|
||||
if imgui.begin_tab_item("Beads")[0]:
|
||||
self._render_beads_tab()
|
||||
imgui.end_tab_item()
|
||||
imgui.end_tab_bar()
|
||||
|
||||
def _render_task_dag_panel(self) -> None:
|
||||
# 4. Task DAG Visualizer
|
||||
imgui.text("Task DAG")
|
||||
if self.active_track and self.node_editor_ctx:
|
||||
if (self.active_track or self.active_tickets) and self.node_editor_ctx:
|
||||
ed.set_current_editor(self.node_editor_ctx)
|
||||
ed.begin('Visual DAG')
|
||||
# Selection detection
|
||||
@@ -4470,6 +4487,9 @@ def hello():
|
||||
tid = str(t.get('id', '??'))
|
||||
int_id = abs(hash(tid))
|
||||
ed.begin_node(ed.NodeId(int_id))
|
||||
if getattr(self, "ui_project_execution_mode", "native") == "beads":
|
||||
imgui.text_colored(imgui.ImVec4(0, 1, 1, 1), "[B] ")
|
||||
imgui.same_line()
|
||||
imgui.text_colored(C_KEY, f"Ticket: {tid}")
|
||||
status = t.get('status', 'todo')
|
||||
s_col = C_VAL
|
||||
@@ -4590,7 +4610,48 @@ def hello():
|
||||
self._show_add_ticket_form = False
|
||||
imgui.end_child()
|
||||
else:
|
||||
imgui.text_disabled("No active MMA track.")
|
||||
imgui.text_disabled("No active MMA track or tickets.")
|
||||
|
||||
def _render_beads_tab(self) -> None:
|
||||
imgui.text("Beads Graph (Dolt-backed)")
|
||||
if imgui.button("Refresh Beads"):
|
||||
pass
|
||||
imgui.separator()
|
||||
|
||||
# Check for dolt/bd dependencies
|
||||
dolt_path = shutil.which("dolt")
|
||||
bd_path = shutil.which("bd")
|
||||
if not dolt_path or not bd_path:
|
||||
missing = []
|
||||
if not dolt_path: missing.append("'dolt'")
|
||||
if not bd_path: missing.append("'bd'")
|
||||
imgui.text_colored(imgui.ImVec4(1, 0.5, 0, 1), f"Warning: {', '.join(missing)} not found in PATH.")
|
||||
imgui.text_wrapped("Beads mode requires Dolt and the Beads (bd) CLI tools.")
|
||||
|
||||
if getattr(self, "ui_project_execution_mode", "native") == "beads":
|
||||
try:
|
||||
from src import beads_client
|
||||
bclient = beads_client.BeadsClient(Path(self.active_project_root))
|
||||
beads = bclient.list_beads()
|
||||
if not beads:
|
||||
imgui.text_disabled("No beads found.")
|
||||
else:
|
||||
if imgui.begin_table("beads_table", 3, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable):
|
||||
imgui.table_setup_column("ID")
|
||||
imgui.table_setup_column("Status")
|
||||
imgui.table_setup_column("Title")
|
||||
imgui.table_headers_row()
|
||||
for b in beads:
|
||||
imgui.table_next_row()
|
||||
imgui.table_next_column()
|
||||
imgui.text(str(b.id))
|
||||
imgui.table_next_column()
|
||||
imgui.text(str(b.status))
|
||||
imgui.table_next_column()
|
||||
imgui.text(str(b.title))
|
||||
imgui.end_table()
|
||||
except Exception as e:
|
||||
imgui.text_colored(imgui.ImVec4(1, 0, 0, 1), f"Error loading beads: {e}")
|
||||
|
||||
def _render_tier_stream_panel(self, tier_key: str, stream_key: str | None) -> None:
|
||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_tier_stream_panel")
|
||||
|
||||
Reference in New Issue
Block a user