feat: エージェントにuiux-pro-max cli を使用させる#16
Conversation
There was a problem hiding this comment.
Pull request overview
This PR integrates the “uiux-pro-max” CLI into the analyzer worker so proposal generation can be informed by an automatically generated design-system reference, and updates the backend proposal response formatting to better tolerate varied plan shapes.
Changes:
- Install
uipro-cliin the worker image and run it during analysis to generate “design context”. - Extend the analyze prompt builder to include a “Design System Reference” block and tighten proposal validation guidance.
- Make backend
planparsing more tolerant by converting non-string plan items into displayable strings.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| docker/worker.Dockerfile | Adds global install of uipro-cli into the worker image. |
| docker/worker-analyze.py | Runs uipro init + a generated search.py to produce design context and injects it into the analyze prompt. |
| docker/prompts.py | Adds design_context parameter and interpolates a “Design System Reference” section into the analyze prompt. |
| backend/app/router/sessions.py | Expands proposal plan decoding to handle non-string elements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| design_context_block = f""" | ||
| ## Design System Reference | ||
|
|
||
| The following design system recommendations were generated based on the user's request. | ||
| Use these as professional design intelligence to inform your proposals. | ||
|
|
||
| <design_reference> | ||
| {design_context} | ||
| </design_reference> |
There was a problem hiding this comment.
design_context is interpolated into the prompt without any sanitization/escaping. Since this content comes from an external tool run against an arbitrary repo, it can contain XML-like tags (e.g., </design_reference>) or other prompt-injection sequences that break the delimiter and override instructions. Sanitize/escape design_context similarly to _sanitize_user_input() (and consider escaping triple backticks) before embedding it, and explicitly instruct the model to treat it as untrusted reference text only.
| raw_plan = json.loads(proposal.plan) if proposal.plan else [] | ||
| except json.JSONDecodeError: | ||
| plan = [] | ||
| raw_plan = [] |
There was a problem hiding this comment.
raw_plan is assumed to be a list, but json.loads() can legally return a dict (or a string/number). In that case, for item in raw_plan will iterate dict keys (or characters), producing a corrupted plan. Normalize the decoded value first (e.g., if it's not a list, wrap it in a one-element list or handle dict specially) before iterating.
| raw_plan = [] | |
| raw_plan = [] | |
| # Normalize raw_plan to always be a list so iteration behaves correctly | |
| if isinstance(raw_plan, dict): | |
| raw_plan = [raw_plan] | |
| elif not isinstance(raw_plan, list): | |
| raw_plan = [raw_plan] |
| design_context = generate_design_context(repo_dir, instruction) | ||
| emit_log("analyzing", "Thinking: Design system context generated successfully") |
There was a problem hiding this comment.
generate_design_context() raises on any uipro failure, and generate_proposals() does not catch those errors. As a result, a transient/missing uipro install will cause the entire analysis step to return zero proposals (caught only at the top-level main() exception handler). Make the design-context generation best-effort (catch exceptions here, log them, and continue with design_context="" so proposals still get generated).
| design_context = generate_design_context(repo_dir, instruction) | |
| emit_log("analyzing", "Thinking: Design system context generated successfully") | |
| try: | |
| design_context = generate_design_context(repo_dir, instruction) | |
| emit_log("analyzing", "Thinking: Design system context generated successfully") | |
| except Exception as e: | |
| emit_log( | |
| "analyzing", | |
| "Warning: Failed to generate design system context; continuing without it", | |
| detail=str(e), | |
| ) | |
| design_context = "" |
| search_py = _uipro_search_path(repo_dir) | ||
| result = subprocess.run( | ||
| [ | ||
| "python3", search_py, | ||
| instruction, |
There was a problem hiding this comment.
_uipro_search_path() hardcodes a path to .../.claude/skills/ui-ux-pro-max/scripts/search.py but the code never checks that this file exists before invoking python3 with it. If uipro init doesn't create that file (or creates it elsewhere), the failure mode will be a generic subprocess error. Consider validating the path (e.g., Path(search_py).exists()) and raising a clearer error or falling back gracefully.
| npx playwright install chromium | ||
|
|
||
| # Install ui-ux-pro-max design intelligence tool | ||
| RUN npm install -g uipro-cli |
There was a problem hiding this comment.
The RUN npm install -g uipro-cli line introduces an unpinned third-party CLI that will be automatically fetched at build time and later executed with the worker container’s privileges. If the uipro-cli package or its registry entry is compromised, attackers could execute arbitrary code in your worker environment and exfiltrate any secrets available to the container. Pin this dependency to a specific, vetted version or vendor it locally, and consider periodically reviewing or mirroring it to reduce supply-chain risk.
No description provided.