This commit is contained in:
2026-03-06 23:07:08 -05:00
parent cb57cc4a02
commit 4921a6715c
7 changed files with 51 additions and 44 deletions

View File

@@ -1,4 +1,4 @@
# mcp_client.py
# mcp_client.py
"""
Note(Gemini):
MCP-style file context tools for manual_slop.
@@ -322,7 +322,7 @@ def set_file_slice(path: str, start_line: int, end_line: int, new_content: str)
new_content += "\n"
new_lines = new_content.splitlines(keepends=True) if new_content else []
lines[start_idx:end_idx] = new_lines
p.write_text("".join(lines), encoding="utf-8", newline="")
p.write_text("".join(lines), encoding="utf-8")
return f"Successfully updated lines {start_line}-{end_line} in {path}"
except Exception as e:
return f"ERROR updating slice in '{path}': {e}"
@@ -341,7 +341,7 @@ def edit_file(path: str, old_string: str, new_string: str, replace_all: bool = F
if not old_string:
return "ERROR: old_string cannot be empty"
try:
content = p.read_text(encoding="utf-8", newline="")
content = p.read_text(encoding="utf-8")
if old_string not in content:
return f"ERROR: old_string not found in '{path}'"
count = content.count(old_string)
@@ -349,11 +349,11 @@ def edit_file(path: str, old_string: str, new_string: str, replace_all: bool = F
return f"ERROR: Found {count} matches for old_string in '{path}'. Use replace_all=true or provide more context to make it unique."
if replace_all:
new_content = content.replace(old_string, new_string)
p.write_text(new_content, encoding="utf-8", newline="")
p.write_text(new_content, encoding="utf-8")
return f"Successfully replaced {count} occurrences in '{path}'"
else:
new_content = content.replace(old_string, new_string, 1)
p.write_text(new_content, encoding="utf-8", newline="")
p.write_text(new_content, encoding="utf-8")
return f"Successfully replaced 1 occurrence in '{path}'"
except Exception as e:
return f"ERROR editing '{path}': {e}"
@@ -734,10 +734,10 @@ def get_tree(path: str, max_depth: int = 2) -> str:
entries = [e for e in entries if not e.name.startswith('.') and e.name not in ('__pycache__', 'venv', 'env') and e.name != "history.toml" and not e.name.endswith("_history.toml")]
for i, entry in enumerate(entries):
is_last = (i == len(entries) - 1)
connector = "└── " if is_last else "├── "
connector = "└── " if is_last else "├── "
lines.append(f"{prefix}{connector}{entry.name}")
if entry.is_dir():
extension = " " if is_last else " "
extension = " " if is_last else "│ "
lines.extend(_build_tree(entry, current_depth + 1, prefix + extension))
return lines
tree_lines = [f"{p.name}/"] + _build_tree(p, 1)
@@ -1416,3 +1416,5 @@ MCP_TOOL_SPECS: list[dict[str, Any]] = [
}
]

View File

@@ -17,7 +17,6 @@ def save_config(config: dict[str, Any]) -> None:
with open(CONFIG_PATH, "wb") as f:
tomli_w.dump(config, f)
# Global constants for agent tools
AGENT_TOOL_NAMES = [
"run_powershell",
"read_file",
@@ -40,7 +39,6 @@ AGENT_TOOL_NAMES = [
]
def parse_history_entries(history_strings: list[str], roles: list[str]) -> list[dict[str, Any]]:
"""Parse stored history strings back to disc entry dicts."""
import re
entries = []
for raw in history_strings:
@@ -64,10 +62,6 @@ def parse_history_entries(history_strings: list[str], roles: list[str]) -> list[
@dataclass
class Ticket:
"""
Represents a discrete unit of work within a track.
"""
id: str
description: str
status: str = "todo"
@@ -81,16 +75,13 @@ class Ticket:
retry_count: int = 0
def mark_blocked(self, reason: str) -> None:
"""Sets the ticket status to 'blocked' and records the reason."""
self.status = "blocked"
self.blocked_reason = reason
def mark_complete(self) -> None:
"""Sets the ticket status to 'completed'."""
self.status = "completed"
def get(self, key: str, default: Any = None) -> Any:
"""Helper to provide dictionary-like access to dataclass fields."""
return getattr(self, key, default)
def to_dict(self) -> Dict[str, Any]:
@@ -127,16 +118,11 @@ class Ticket:
@dataclass
class Track:
"""
Represents a collection of tickets that together form an architectural track or epic.
"""
id: str
description: str
tickets: List[Ticket] = field(default_factory=list)
def get_executable_tickets(self) -> List[Ticket]:
"""Returns tickets that are ready to be executed (dependencies met)."""
from src.dag_engine import TrackDAG
dag = TrackDAG(self.tickets)
return dag.get_ready_tasks()
@@ -159,10 +145,6 @@ class Track:
@dataclass
class WorkerContext:
"""
State preserved for a specific worker throughout its ticket lifecycle.
"""
ticket_id: str
model_name: str
messages: List[Dict[str, Any]] = field(default_factory=list)
@@ -172,17 +154,17 @@ class WorkerContext:
class Metadata:
id: str
name: str
status: str
created_at: Union[str, Any]
updated_at: Union[str, Any]
status: Optional[str] = None
created_at: Optional[datetime.datetime] = None
updated_at: Optional[datetime.datetime] = None
def to_dict(self) -> Dict[str, Any]:
return {
"id": self.id,
"name": self.name,
"status": self.status,
"created_at": str(self.created_at),
"updated_at": str(self.updated_at),
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
@classmethod
@@ -193,16 +175,16 @@ class Metadata:
try:
created = datetime.datetime.fromisoformat(created)
except ValueError:
pass
created = None
if isinstance(updated, str):
try:
updated = datetime.datetime.fromisoformat(updated)
except ValueError:
pass
updated = None
return cls(
id=data["id"],
name=data.get("name", ""),
status=data.get("status", "todo"),
status=data.get("status"),
created_at=created,
updated_at=updated,
)
@@ -215,16 +197,39 @@ class TrackState:
tasks: List[Ticket] = field(default_factory=list)
def to_dict(self) -> Dict[str, Any]:
serialized_discussion = []
for item in self.discussion:
if isinstance(item, dict):
new_item = dict(item)
if "ts" in new_item and isinstance(new_item["ts"], datetime.datetime):
new_item["ts"] = new_item["ts"].isoformat()
serialized_discussion.append(new_item)
else:
serialized_discussion.append(item)
return {
"metadata": self.metadata.to_dict(),
"discussion": self.discussion,
"discussion": serialized_discussion,
"tasks": [t.to_dict() for t in self.tasks],
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "TrackState":
discussion = data.get("discussion", [])
parsed_discussion = []
for item in discussion:
if isinstance(item, dict):
new_item = dict(item)
ts = new_item.get("ts")
if isinstance(ts, str):
try:
new_item["ts"] = datetime.datetime.fromisoformat(ts)
except ValueError:
pass
parsed_discussion.append(new_item)
else:
parsed_discussion.append(item)
return cls(
metadata=Metadata.from_dict(data["metadata"]),
discussion=data.get("discussion", []),
discussion=parsed_discussion,
tasks=[Ticket.from_dict(t) for t in data.get("tasks", [])],
)