151 lines
4.6 KiB
Python
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())
|
|
|