feat(beads): integrate Beads Mode backend, MCP tools, and GUI support
This commit is contained in:
+36
-5
@@ -19,6 +19,7 @@ from pathlib import Path, PureWindowsPath
|
||||
from typing import Any, cast
|
||||
from src import summarize
|
||||
from src import project_manager
|
||||
from src import beads_client
|
||||
from src.file_cache import ASTParser
|
||||
|
||||
def find_next_increment(output_dir: Path, namespace: str) -> int:
|
||||
@@ -197,7 +198,28 @@ def _build_files_section_from_items(file_items: list[dict[str, Any]]) -> str:
|
||||
sections.append(f"### `{original}`\n\n```{lang}\n{content}\n```")
|
||||
return "\n\n---\n\n".join(sections)
|
||||
|
||||
def build_markdown_from_items(file_items: list[dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], history: list[str], summary_only: bool = False, aggregation_strategy: str = "auto") -> str:
|
||||
def build_beads_section(base_dir: Path) -> str:
|
||||
client = beads_client.BeadsClient(base_dir)
|
||||
if not client.is_initialized():
|
||||
return ""
|
||||
beads = client.list_beads()
|
||||
if not beads:
|
||||
return ""
|
||||
active = [b for b in beads if b.status == "active"]
|
||||
completed = [b for b in beads if b.status == "completed"]
|
||||
parts = []
|
||||
parts.append("## Beads Mode: Progress Track")
|
||||
if completed:
|
||||
parts.append("### Completed Beads")
|
||||
comp_list = ", ".join([f"`{b.title}`" for b in completed])
|
||||
parts.append(comp_list)
|
||||
if active:
|
||||
parts.append("### Active Beads")
|
||||
for b in active:
|
||||
parts.append(f"- **{b.title}** ({b.id}): {b.description}")
|
||||
return "\n\n".join(parts)
|
||||
|
||||
def build_markdown_from_items(file_items: list[dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], history: list[str], summary_only: bool = False, aggregation_strategy: str = "auto", execution_mode: str = "standard", base_dir: Path | None = None) -> str:
|
||||
"""Build markdown from pre-read file items instead of re-reading from disk."""
|
||||
parts = []
|
||||
# STATIC PREFIX: Files and Screenshots must go first to maximize Cache Hits
|
||||
@@ -213,7 +235,11 @@ def build_markdown_from_items(file_items: list[dict[str, Any]], screenshot_base_
|
||||
parts.append("## Files\n\n" + _build_files_section_from_items(file_items))
|
||||
if screenshots:
|
||||
parts.append("## Screenshots\n\n" + build_screenshots_section(screenshot_base_dir, screenshots))
|
||||
# DYNAMIC SUFFIX: History changes every turn, must go last
|
||||
if execution_mode == "beads" and base_dir:
|
||||
beads_md = build_beads_section(base_dir)
|
||||
if beads_md:
|
||||
parts.append(beads_md)
|
||||
# DYNAMIC SUFFIX: History changes every turn, must go last
|
||||
if history:
|
||||
parts.append("## Discussion History\n\n" + build_discussion_section(history))
|
||||
return "\n\n---\n\n".join(parts)
|
||||
@@ -309,7 +335,7 @@ def build_tier3_context(file_items: list[dict[str, Any]], screenshot_base_dir: P
|
||||
parts.append("## Discussion History\n\n" + build_discussion_section(history))
|
||||
return "\n\n---\n\n".join(parts)
|
||||
|
||||
def build_markdown(base_dir: Path, files: list[str | dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], history: list[str], summary_only: bool = False) -> str:
|
||||
def build_markdown(base_dir: Path, files: list[str | dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], history: list[str], summary_only: bool = False, execution_mode: str = "standard") -> str:
|
||||
parts = []
|
||||
# STATIC PREFIX: Files and Screenshots must go first to maximize Cache Hits
|
||||
if files:
|
||||
@@ -319,7 +345,11 @@ def build_markdown(base_dir: Path, files: list[str | dict[str, Any]], screenshot
|
||||
parts.append("## Files\n\n" + build_files_section(base_dir, files))
|
||||
if screenshots:
|
||||
parts.append("## Screenshots\n\n" + build_screenshots_section(screenshot_base_dir, screenshots))
|
||||
# DYNAMIC SUFFIX: History changes every turn, must go last
|
||||
if execution_mode == "beads":
|
||||
beads_md = build_beads_section(base_dir)
|
||||
if beads_md:
|
||||
parts.append(beads_md)
|
||||
# DYNAMIC SUFFIX: History changes every turn, must go last
|
||||
if history:
|
||||
parts.append("## Discussion History\n\n" + build_discussion_section(history))
|
||||
return "\n\n---\n\n".join(parts)
|
||||
@@ -340,8 +370,9 @@ def run(config: dict[str, Any], aggregation_strategy: str = "auto") -> tuple[str
|
||||
# Build file items once, then construct markdown from them (avoids double I/O)
|
||||
file_items = build_file_items(base_dir, files)
|
||||
summary_only = config.get("project", {}).get("summary_only", False)
|
||||
execution_mode = config.get("project", {}).get("execution_mode", "standard")
|
||||
markdown = build_markdown_from_items(file_items, screenshot_base_dir, screenshots, history,
|
||||
summary_only=summary_only, aggregation_strategy=aggregation_strategy)
|
||||
summary_only=summary_only, aggregation_strategy=aggregation_strategy, execution_mode=execution_mode, base_dir=base_dir)
|
||||
output_file.write_text(markdown, encoding="utf-8")
|
||||
return markdown, output_file, file_items
|
||||
|
||||
|
||||
+44
-2
@@ -234,6 +234,7 @@ class AppController:
|
||||
self.ui_project_git_dir: str = ""
|
||||
self.ui_project_main_context: str = ""
|
||||
self.ui_project_system_prompt: str = ""
|
||||
self.ui_project_execution_mode: str = "native"
|
||||
self.ui_gemini_cli_path: str = "gemini"
|
||||
self.ui_word_wrap: bool = True
|
||||
self.ui_auto_add_history: bool = False
|
||||
@@ -954,6 +955,25 @@ class AppController:
|
||||
elapsed = end_time - start_time
|
||||
self._completed_ticket_count += 1
|
||||
self._avg_ticket_time = ((self._avg_ticket_time * (self._completed_ticket_count - 1)) + elapsed) / self._completed_ticket_count
|
||||
elif action == "bead_updated":
|
||||
payload = task.get("payload", {})
|
||||
bid = payload.get("bead_id")
|
||||
status = payload.get("status")
|
||||
if bid and status:
|
||||
stream_id = "Tier 2"
|
||||
msg = f"\n[BEAD UPDATE] {bid} -> status: {status}\n"
|
||||
if stream_id not in self.mma_streams:
|
||||
self.mma_streams[stream_id] = ""
|
||||
self.mma_streams[stream_id] += msg
|
||||
|
||||
elif action == "bead_updated":
|
||||
payload = task.get("payload", {})
|
||||
bead_id = payload.get("bead_id")
|
||||
status = payload.get("status")
|
||||
stream_id = "Tier 2 (Tech Lead)"
|
||||
if stream_id not in self.mma_streams:
|
||||
self.mma_streams[stream_id] = ""
|
||||
self.mma_streams[stream_id] += f"[BEAD UPDATE] {bead_id} -> status: {status}\n"
|
||||
except Exception as e:
|
||||
import traceback
|
||||
sys.stderr.write(f"[DEBUG] Error executing GUI task: {e}\n{traceback.format_exc()}\n")
|
||||
@@ -2367,8 +2387,8 @@ class AppController:
|
||||
description=state.metadata.name,
|
||||
tickets=tickets
|
||||
)
|
||||
# Keep dicts for UI table (or convert models.Ticket objects back to dicts if needed)
|
||||
self.active_tickets = [asdict(t) if not isinstance(t, dict) else t for t in tickets]
|
||||
# Keep dicts for UI table
|
||||
self._load_active_tickets()
|
||||
# Load track-scoped history
|
||||
history = project_manager.load_track_history(track_id, self.active_project_root)
|
||||
with self._disc_entries_lock:
|
||||
@@ -3143,4 +3163,26 @@ class AppController:
|
||||
)
|
||||
project_manager.save_track_state(self.active_track.id, state, self.active_project_root)
|
||||
|
||||
def _load_active_tickets(self) -> None:
|
||||
"""Populates self.active_tickets based on the current execution mode."""
|
||||
if getattr(self, "ui_project_execution_mode", "native") == "beads":
|
||||
from src import beads_client
|
||||
bclient = beads_client.BeadsClient(Path(self.active_project_root))
|
||||
beads = bclient.list_beads()
|
||||
self.active_tickets = []
|
||||
for b in beads:
|
||||
self.active_tickets.append({
|
||||
"id": b.id,
|
||||
"title": b.title,
|
||||
"description": b.description,
|
||||
"status": b.status,
|
||||
"assigned_to": "tier3-worker",
|
||||
"target_file": "",
|
||||
"depends_on": []
|
||||
})
|
||||
else:
|
||||
if self.active_track:
|
||||
self.active_tickets = [asdict(t) if not isinstance(t, dict) else t for t in self.active_track.tickets]
|
||||
else:
|
||||
self.active_tickets = []
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
@dataclass
|
||||
class Bead:
|
||||
id: str
|
||||
title: str
|
||||
description: str
|
||||
status: str = "active"
|
||||
|
||||
class BeadsClient:
|
||||
def __init__(self, working_dir: Path):
|
||||
self.working_dir = Path(working_dir)
|
||||
self.repo_dir = self.working_dir / ".beads_mock"
|
||||
self.beads_file = self.repo_dir / "beads.json"
|
||||
|
||||
def init_repo(self) -> None:
|
||||
"""Initialize the mock repository."""
|
||||
self.repo_dir.mkdir(parents=True, exist_ok=True)
|
||||
if not self.beads_file.exists():
|
||||
self.beads_file.write_text("[]", encoding="utf-8")
|
||||
|
||||
def is_initialized(self) -> bool:
|
||||
"""Check if the repository is initialized."""
|
||||
return self.beads_file.exists()
|
||||
|
||||
def create_bead(self, title: str, description: str) -> str:
|
||||
"""Create a new bead and return its ID."""
|
||||
beads = self._read_beads()
|
||||
bead_id = f"bead-{len(beads) + 1}"
|
||||
bead = {"id": bead_id, "title": title, "description": description, "status": "active"}
|
||||
beads.append(bead)
|
||||
self._write_beads(beads)
|
||||
return bead_id
|
||||
|
||||
def update_bead(self, bead_id: str, status: str) -> bool:
|
||||
"""Update the status of an existing bead."""
|
||||
beads = self._read_beads()
|
||||
for bead in beads:
|
||||
if bead["id"] == bead_id:
|
||||
bead["status"] = status
|
||||
self._write_beads(beads)
|
||||
return True
|
||||
return False
|
||||
|
||||
def list_beads(self) -> List[Bead]:
|
||||
"""List all beads."""
|
||||
return [Bead(**b) for b in self._read_beads()]
|
||||
|
||||
def _read_beads(self) -> List[dict]:
|
||||
if not self.beads_file.exists():
|
||||
return []
|
||||
return json.loads(self.beads_file.read_text(encoding="utf-8"))
|
||||
|
||||
def _write_beads(self, beads: List[dict]) -> None:
|
||||
self.beads_file.write_text(json.dumps(beads, indent=1), encoding="utf-8")
|
||||
+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")
|
||||
|
||||
@@ -62,6 +62,7 @@ import ast
|
||||
import subprocess
|
||||
from src import summarize
|
||||
from src import outline_tool
|
||||
from src import beads_client
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
from html.parser import HTMLParser
|
||||
@@ -1282,6 +1283,31 @@ def dispatch(tool_name: str, tool_input: dict[str, Any]) -> str:
|
||||
return py_get_docstring(path, str(tool_input.get("name", "")))
|
||||
if tool_name == "get_tree":
|
||||
return get_tree(path, int(tool_input.get("max_depth", 2)))
|
||||
|
||||
# Beads tools
|
||||
if tool_name.startswith("bd_"):
|
||||
if not _primary_base_dir:
|
||||
return "ERROR: no active workspace to run beads tools."
|
||||
bclient = beads_client.BeadsClient(_primary_base_dir)
|
||||
if tool_name == "bd_list":
|
||||
beads = bclient.list_beads()
|
||||
if not beads:
|
||||
return "No beads found."
|
||||
return "\n".join([f"ID: {b.id}, Status: {b.status}, Title: {b.title}" for b in beads])
|
||||
elif tool_name == "bd_create":
|
||||
title = str(tool_input.get("title", ""))
|
||||
desc = str(tool_input.get("description", ""))
|
||||
bid = bclient.create_bead(title, desc)
|
||||
return f"Created bead: {bid}"
|
||||
elif tool_name == "bd_update":
|
||||
bid = str(tool_input.get("bead_id", ""))
|
||||
status = str(tool_input.get("status", ""))
|
||||
if bclient.update_bead(bid, status):
|
||||
return f"Updated {bid} to status {status}"
|
||||
return f"ERROR: bead {bid} not found."
|
||||
elif tool_name == "bd_ready":
|
||||
return "READY" if bclient.is_initialized() else "NOT_INITIALIZED"
|
||||
|
||||
return f"ERROR: unknown MCP tool '{tool_name}'"
|
||||
|
||||
async def async_dispatch(tool_name: str, tool_input: dict[str, Any]) -> str:
|
||||
@@ -1967,6 +1993,46 @@ MCP_TOOL_SPECS: list[dict[str, Any]] = [
|
||||
},
|
||||
"required": ["path"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bd_create",
|
||||
"description": "Create a new Bead in the active Beads repository.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": { "type": "string", "description": "Title of the Bead." },
|
||||
"description": { "type": "string", "description": "Description of the Bead." }
|
||||
},
|
||||
"required": ["title", "description"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bd_update",
|
||||
"description": "Update an existing Bead.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bead_id": { "type": "string", "description": "ID of the Bead to update." },
|
||||
"status": { "type": "string", "description": "New status for the Bead." }
|
||||
},
|
||||
"required": ["bead_id", "status"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bd_list",
|
||||
"description": "List all Beads in the active Beads repository.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bd_ready",
|
||||
"description": "Check if the Beads repository is initialized in the current workspace.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ def default_discussion() -> dict[str, Any]:
|
||||
|
||||
def default_project(name: str = "unnamed") -> dict[str, Any]:
|
||||
return {
|
||||
"project": {"name": name, "git_dir": "", "system_prompt": "", "main_context": ""},
|
||||
"project": {"name": name, "git_dir": "", "system_prompt": "", "main_context": "", "execution_mode": "native"},
|
||||
"output": {"output_dir": "./md_gen"},
|
||||
"files": {"base_dir": ".", "paths": [], "tier_assignments": {}},
|
||||
"screenshots": {"base_dir": ".", "paths": []},
|
||||
|
||||
Reference in New Issue
Block a user