Files
voidea/app/agents/backlog_agent.py
T

285 lines
8.8 KiB
Python

"""BacklogAgent - Backlog management for VoIdea.
This agent:
- Creates and manages backlog items
- Tracks ideas, plans, tasks
- Prioritizes work
- Sends reminders
"""
import re
from datetime import datetime, timezone
from typing import Any
from uuid import UUID, uuid4
from sqlalchemy import select, update, delete
from sqlalchemy.ext.asyncio import AsyncSession
from app.agents.base import AgentResult, AgentStatus, AgentTrigger, BaseAgent
from app.agents.models import BacklogItem
class BacklogAgent(BaseAgent):
"""Backlog management agent."""
name = "backlog_agent"
version = "1.0.0"
description = "Manages project backlog: ideas, tasks, plans"
triggers = [
AgentTrigger.MANUAL,
AgentTrigger.CRON,
AgentTrigger.EVENT,
]
def __init__(self, session: AsyncSession | None = None):
super().__init__()
self._session = session
async def run(self, context: dict[str, Any] | None = None) -> AgentResult:
"""Execute backlog management task.
Context can contain:
- action: str (create, list, update, delete, suggest)
- item_type: str (idea, plan, task, improvement)
- title: str
- description: str
- priority: str (low, medium, high, critical)
- source: str (opencode, admin_panel, user, agent)
"""
await self.set_running("backlog_management")
try:
action = context.get("action", "list") if context else "list"
if action == "create":
result = await self._create_item(context or {})
elif action == "list":
result = await self._list_items(context or {})
elif action == "update":
result = await self._update_item(context or {})
elif action == "delete":
result = await self._delete_item(context or {})
elif action == "suggest":
result = await self._suggest_items()
else:
result = await self._list_items({})
await self.set_idle()
return result
except Exception as e:
await self.set_error(str(e))
return AgentResult(
success=False,
message=f"BacklogAgent failed: {str(e)}",
errors=[str(e)],
)
async def health_check(self) -> bool:
"""Check if BacklogAgent is operational."""
return True
async def _create_item(self, context: dict[str, Any]) -> AgentResult:
"""Create a new backlog item."""
if not self._session:
return AgentResult(
success=False,
message="Database session not configured",
)
item_type = context.get("item_type", "task")
title = context.get("title", "Untitled")
description = context.get("description", "")
priority = context.get("priority", "medium")
source = context.get("source", "agent")
created_by = context.get("created_by", "BacklogAgent")
tags = context.get("tags", [])
block_ref = context.get("block_ref")
item = BacklogItem(
id=uuid4(),
item_type=item_type,
title=title,
description=description,
priority=priority,
status="pending",
source=source,
created_by=created_by,
tags=tags,
block_ref=block_ref,
)
self._session.add(item)
await self._session.commit()
return AgentResult(
success=True,
message=f"Created backlog item: {title}",
data={
"id": str(item.id),
"type": item_type,
"title": title,
"priority": priority,
},
)
async def _list_items(
self,
context: dict[str, Any],
limit: int = 50,
) -> AgentResult:
"""List backlog items."""
if not self._session:
return AgentResult(
success=False,
message="Database session not configured",
)
item_type = context.get("item_type")
status_filter = context.get("status")
priority_filter = context.get("priority")
query = select(BacklogItem).order_by(BacklogItem.created_at.desc())
if item_type:
query = query.where(BacklogItem.item_type == item_type)
if status_filter:
query = query.where(BacklogItem.status == status_filter)
if priority_filter:
query = query.where(BacklogItem.priority == priority_filter)
query = query.limit(limit)
result = await self._session.execute(query)
items = result.scalars().all()
items_data = [
{
"id": str(item.id),
"type": item.item_type,
"title": item.title,
"priority": item.priority,
"status": item.status,
"created_at": item.created_at.isoformat() if item.created_at else None,
}
for item in items
]
return AgentResult(
success=True,
message=f"Found {len(items)} backlog items",
data={"items": items_data, "count": len(items)},
)
async def _update_item(self, context: dict[str, Any]) -> AgentResult:
"""Update a backlog item."""
if not self._session:
return AgentResult(
success=False,
message="Database session not configured",
)
item_id = context.get("id")
if not item_id:
return AgentResult(
success=False,
message="Item ID required for update",
)
update_data = {}
for field in ["title", "description", "priority", "status", "tags"]:
if field in context:
update_data[field] = context[field]
if update_data:
stmt = (
update(BacklogItem)
.where(BacklogItem.id == UUID(item_id))
.values(**update_data)
)
await self._session.execute(stmt)
await self._session.commit()
return AgentResult(
success=True,
message=f"Updated backlog item: {item_id}",
data={"id": item_id, "updated": update_data},
)
async def _delete_item(self, context: dict[str, Any]) -> AgentResult:
"""Delete a backlog item."""
if not self._session:
return AgentResult(
success=False,
message="Database session not configured",
)
item_id = context.get("id")
if not item_id:
return AgentResult(
success=False,
message="Item ID required for delete",
)
stmt = delete(BacklogItem).where(BacklogItem.id == UUID(item_id))
await self._session.execute(stmt)
await self._session.commit()
return AgentResult(
success=True,
message=f"Deleted backlog item: {item_id}",
data={"id": item_id},
)
async def _suggest_items(self) -> AgentResult:
"""Suggest backlog items based on project needs."""
suggestions = [
{
"type": "task",
"title": "Set up PostgreSQL local database",
"priority": "high",
"reason": "Required for Block 2: Data",
},
{
"type": "task",
"title": "Configure environment variables",
"priority": "medium",
"reason": "Prerequisite for running app",
},
{
"type": "improvement",
"title": "Add pre-commit hooks",
"priority": "low",
"reason": "Improves code quality",
},
]
return AgentResult(
success=True,
message="Generated backlog suggestions",
data={"suggestions": suggestions},
)
async def get_metrics(self) -> dict[str, Any]:
"""Get BacklogAgent metrics."""
if not self._session:
return {
"agent_id": self.name,
"status": self.status.value,
}
try:
pending_count = await self._session.execute(
select(BacklogItem).where(BacklogItem.status == "pending")
)
return {
"agent_id": self.name,
"version": self.version,
"status": self.status.value,
"pending_items": pending_count.scalars().count(),
}
except Exception:
return {
"agent_id": self.name,
"status": self.status.value,
}