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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user