#!/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())