CascadingDev/agents/designer.py

151 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""
AI_Designer agent.
Provides UX-focused feedback in active discussions, complementing the moderator
with design guidance and context. Posts a fresh comment on every run so the
conversation reflects the latest review.
"""
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
from textwrap import dedent
def load_agent_context(repo_root: Path):
vendor_root = repo_root / ".cascadingdev"
if (vendor_root / "cascadingdev" / "agent").exists():
sys.path.insert(0, str(vendor_root))
from cascadingdev.agent.sdk import AgentContext
return AgentContext
project_root = Path(__file__).resolve().parents[1]
src_path = project_root / "src"
if src_path.exists():
sys.path.insert(0, str(src_path))
from cascadingdev.agent.sdk import AgentContext
return AgentContext
from cascadingdev.agent.sdk import AgentContext
return AgentContext
def load_provider_client(repo_root: Path):
vendor_root = repo_root / ".cascadingdev"
if (vendor_root / "cascadingdev" / "agent").exists():
sys.path.insert(0, str(vendor_root))
from cascadingdev.agent.providers import ProviderClient
return ProviderClient
project_root = Path(__file__).resolve().parents[1]
src_path = project_root / "src"
if src_path.exists():
sys.path.insert(0, str(src_path))
from cascadingdev.agent.providers import ProviderClient
return ProviderClient
from cascadingdev.agent.providers import ProviderClient
return ProviderClient
PRIMER = dedent(
"""
You are AI_Designer, a senior UX designer collaborating with the CascadingDev
team.
Responsibilities:
- Evaluate usability, accessibility, and visual clarity implications raised in
the discussion.
- Flag missing design artefacts or user flows that need clarification.
- Encourage positive collaboration while highlighting potential UX risks.
Instructions:
- Return JSON with keys: "comment" (Markdown body only, no Name/VOTE lines) and
"vote" (READY/CHANGES/REJECT).
- Prefer concise, actionable guidance. Suggest concrete follow-ups when voting
CHANGES.
- Never include literal strings starting with "Name:" or "VOTE:" in the comment
body.
"""
).strip()
def build_prompt(discussion_text: str) -> str:
return f"{PRIMER}\n\nCurrent discussion:\n\n{discussion_text}\n\nProvide your JSON response now."
def scrub_comment(text: str) -> str:
cleaned: list[str] = []
for line in text.splitlines():
stripped = line.strip()
cap = stripped.upper()
if cap.startswith("NAME:") or cap.startswith("VOTE:"):
continue
cleaned.append(line)
return "\n".join(cleaned).strip()
def append_designer_comment(context, comment: str, vote: str) -> None:
lines = ["---", "", "Name: AI_Designer"]
body = scrub_comment(comment)
if body:
lines.append(body)
lines.append(f"VOTE: {vote}")
lines.append("")
context.append_block("\n".join(lines))
def ensure_staged(repo_root: Path, relative_path: Path) -> None:
subprocess.run(["git", "add", relative_path.as_posix()], cwd=repo_root, check=False, capture_output=True)
def main() -> int:
parser = argparse.ArgumentParser(description="AI Designer agent")
parser.add_argument("--repo-root", required=True, help="Repository root path")
parser.add_argument("--path", required=True, help="Relative path to discussion file")
args = parser.parse_args()
repo_root = Path(args.repo_root).resolve()
discussion_rel = Path(args.path)
AgentContext = load_agent_context(repo_root)
ProviderClient = load_provider_client(repo_root)
context = AgentContext(repo_root, discussion_rel)
text = context.read_text()
if not text.strip():
return 0
provider = ProviderClient(repo_root)
try:
response = provider.structured(build_prompt(text), model_hint="quality")
except Exception:
response = {
"comment": "From a UX perspective I'd like to see concrete flows or mockups before marking this ready.",
"vote": "CHANGES",
}
comment = str(response.get("comment", "")).strip()
vote = str(response.get("vote", "CHANGES")).strip().upper()
if vote not in {"READY", "CHANGES", "REJECT"}:
vote = "CHANGES"
if not comment and vote == "READY":
# Nothing meaningful to add.
return 0
append_designer_comment(context, comment, vote)
ensure_staged(repo_root, discussion_rel)
return 0
if __name__ == "__main__":
raise SystemExit(main())