Skip to content

feat: add ui-review mode with judge panel support#115

Merged
jacsamell merged 5 commits intomainfrom
feature/ui-review
Mar 10, 2026
Merged

feat: add ui-review mode with judge panel support#115
jacsamell merged 5 commits intomainfrom
feature/ui-review

Conversation

@roy-songzhe-li
Copy link
Copy Markdown
Contributor

@roy-songzhe-li roy-songzhe-li commented Feb 22, 2026

Summary

Adds a standalone cube ui-review command that runs UI/UX reviews through the existing judge panel infrastructure, parallel to the existing pr-review and peer-review modes.

What's new

New command: cube ui-review

# Review one or more UI files (supports cross-page consistency checks)
cube ui-review --file src/pages/Dashboard.tsx

# Review UI changes in a GitHub PR (filters to UI-relevant files only)
cube ui-review --pr 123

# Free-text description
cube ui-review --description "Settings page with 15 ungrouped toggles"

# With context and output
cube ui-review -f src/App.tsx -c "web dashboard, developer users" -o review.md

Flags: --file (repeatable), --description, --pr, --context, --output, --fresh

How it works

  • Loads 7 UI/UX reference docs from automation/ui_review_refs/ as the knowledge base
  • Builds a structured prompt and saves to .prompts/<task-id>.md
  • Runs through the existing launch_judge_panel() with review_type="ui-review"
  • Each judge outputs P0/P1/P2 findings JSON (not APPROVED/REQUEST_CHANGES)
  • Results are merged by priority and displayed with judge attribution
  • Optionally saved to a Markdown file via --output

Changes to existing files

File Change
cli.py +ui-review command registration (~35 lines)
judge_panel.py +ui-review branches: skip worktree prefetch, skip writer branch display, exclude cli-review judges, add P0/P1/P2 status parsing

No changes to existing review modes (pr-review, peer-review, panel, writers).

New files

  • automation/ui_review_refs/ — 7 reference docs (principles, system-principles, design-psych, interaction-psychology, checklists, review-template, icons)
  • automation/ui_review_prompts.py — prompt builder
  • commands/ui_review.py — command logic

Adds a new "ui-review" mode and command to run UI/UX reviews using the existing multi-judge judge-panel infrastructure, parallel to pr-review and peer-review.

Key features

  • New CLI command: cube ui-review with options --file (repeatable), --pr, --description, --context, --output, --post, --fresh.
  • Multiple input modes: one+ UI files, PR-scoped UI diffs, or free-text description; prompts saved to .prompts/.md.
  • Judges produce strict JSON decision files containing P0/P1/P2 findings, quick_wins and summary; findings are merged by priority with judge attribution and optional Markdown export.
  • Knowledge base: seven UI reference docs under python/cube/automation/ui_review_refs/ used to build prompts; prompt builders in python/cube/automation/ui_review_prompts.py (including judge instruction block and decision-file path pattern).
  • PR integration: can filter PR diffs for UI-relevant files and optionally post inline PR comments; github/pulls.fetch_pr gained an optional repo parameter and returns full head SHA for PR flows.

Implementation notes

  • commands/ui_review.py: orchestrates inputs, file reading and headers, PR UI-diff extraction, task_id generation, launching judge panel, collecting/merging P0/P1/P2 findings, formatting/display, saving output, and posting reviews.
  • automation/ui_review_prompts.py: builds the UI review prompt (knowledge base + review-template) and provides judge instructions that require decisions to be written to PROJECT_ROOT/.prompts/decisions/--ui-review.json.
  • automation/judge_panel.py: adds ui-review branches/handling — skips worktree prefetch and writer-branch display for ui-review, excludes cli-review judges for this mode, builds UI-specific judge instructions, and parses P0/P1/P2 statuses.
  • Minor change: core/adapters/cursor.py adds --trust and --print to cursor-agent invocation.

Notes & observations

  • CLI registers the ui-review command once (entry in python/cube/cli.py).
  • UI review judge decisions follow a strict JSON schema and are written to per-task decision files under .prompts/decisions.
  • Existing non-UI review modes (pr-review, peer-review, panel, writers) remain unchanged; ui-review uses guarded branches so it does not interfere with other flows.

- Add 7 UI/UX reference docs in automation/ui_review_refs/
- Add ui_review_prompts.py for building review prompts and judge instructions
- Add commands/ui_review.py with --file, --description, --pr, --output, --fresh flags
- Register cube ui-review command in cli.py
- Extend judge_panel.py with ui-review review_type:
  - Skip worktree prefetch and writer branch checks
  - Exclude cli-review judges (e.g. CodeRabbit) from ui-review
  - Add P0/P1/P2 status parsing in _parse_decision_status
  - Add ui-review header in panel display block

Findings from multiple judges are merged by priority and displayed
with judge attribution. Supports cross-page consistency review via
multiple --file flags. No changes to existing review modes.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a UI-review flow: new prompt/judge instruction builders, UI reference docs, a ui-review CLI command and handler, branching in the judge panel to run UI-only judges and emit P0/P1/P2-formatted decisions, conditional worktree handling for UI reviews, and an extended PR fetch signature supporting repo scope and full head SHA.

Changes

Cohort / File(s) Summary
Judge Panel UI-Review Integration
python/cube/automation/judge_panel.py
Introduces ui-review branching: filters judges for UI-only runs, skips worktree prefetching for UI reviews, uses UI-specific judge instruction blocks, changes decision/status formatting to P0/P1/P2 summaries, and adjusts console output and prompt assembly.
UI Review Prompt Builders
python/cube/automation/ui_review_prompts.py
New module adding build_ui_review_prompt() (assembles KB, context, input section, output-format pointer) and build_ui_review_judge_instructions() (strict JSON schema and path pattern for per-task decision files).
UI Reference Documents
python/cube/automation/ui_review_refs/*
Adds multiple markdown references: checklists.md, design-psych.md, icons.md, interaction-psychology.md, principles.md, review-template.md, system-principles.md — static UX guidance and a review output template used by prompt builders.
UI Review Command Implementation
python/cube/commands/ui_review.py
New command module implementing UI review workflow: file reading, PR UI-diff filtering, PR fetch (via gh), aggregation of multi-judge findings, display/formatting, and optional posting of inline PR comments; exposes ui_review_command().
CLI Integration
python/cube/cli.py
Adds ui-review CLI command wired to ui_review_command() with options for files/description/context/pr/repo/output/post/fresh; note: duplicate ui-review command definition was added (conflict risk).
GitHub PR Fetching
python/cube/github/pulls.py
fetch_pr() signature extended with repo param; GH CLI invocations accept repo scoping flags and the function now returns full head SHA (no truncation).
Minor Runtime/Adapter Change
python/cube/core/adapters/cursor.py
Adds --trust and --print flags to the cursor-agent invocation command.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped in through prompts and docs so bright,

Checklists stacked and JSON set just right.
P0, P1, P2 — tidy and clear,
Judges hum, the UI insights appear,
I nibbled bugs and left a cheerful bite.

🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'feat: add ui-review mode with judge panel support' directly and accurately reflects the main change—adding a new standalone ui-review command with judge panel integration, as confirmed by the PR objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
python/cube/automation/ui_review_prompts.py (1)

33-82: Avoid the unused task_id parameter.

task_id isn’t used (Ruff ARG001). Either remove it or surface it in the prompt for traceability.

♻️ Suggested tweak
-    return f"""# UI Review
-
-You are a UI/UX reviewer. Use the knowledge base below to produce a structured, prioritized review report.
+    return f"""# UI Review
+
+Task ID: {task_id}
+
+You are a UI/UX reviewer. Use the knowledge base below to produce a structured, prioritized review report.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/automation/ui_review_prompts.py` around lines 33 - 82, The
function build_ui_review_prompt currently has an unused parameter task_id
(causing ARG001); either remove task_id from the signature and all callers, or
surface it in the generated prompt for traceability by inserting a short "Review
ID: {task_id}" (or similar) near the top of the returned prompt string (e.g.,
directly after "# UI Review") so the parameter is used; update any callers if
you remove the parameter.
python/cube/commands/ui_review.py (1)

85-134: Filter out CLI-review judges to avoid noisy “missing decision” warnings.

UI review launch excludes cli-review judges, but _collect_findings still iterates all configs, so it warns for judges that never ran. Filtering here keeps output tidy.

♻️ Suggested tweak
-    for jconfig in get_judge_configs():
+    for jconfig in (j for j in get_judge_configs() if j.type != "cli-review"):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/commands/ui_review.py` around lines 85 - 134, _collect_findings
currently iterates all judge configs and prints "No decision file" warnings for
cli-review judges that aren't part of UI runs; filter out CLI-review judges
early to avoid noisy warnings by skipping any jconfig whose key indicates the
CLI reviewer (e.g., if jconfig.key == "cli-review") before computing
get_decision_file_path and processing files—add a guard in the loop inside
_collect_findings to continue for CLI-review configs so only UI-related judges
are checked.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7d2a11 and 532e962.

📒 Files selected for processing (11)
  • python/cube/automation/judge_panel.py
  • python/cube/automation/ui_review_prompts.py
  • python/cube/automation/ui_review_refs/checklists.md
  • python/cube/automation/ui_review_refs/design-psych.md
  • python/cube/automation/ui_review_refs/icons.md
  • python/cube/automation/ui_review_refs/interaction-psychology.md
  • python/cube/automation/ui_review_refs/principles.md
  • python/cube/automation/ui_review_refs/review-template.md
  • python/cube/automation/ui_review_refs/system-principles.md
  • python/cube/cli.py
  • python/cube/commands/ui_review.py
🧰 Additional context used
🧬 Code graph analysis (2)
python/cube/cli.py (1)
python/cube/commands/ui_review.py (1)
  • ui_review_command (215-331)
python/cube/commands/ui_review.py (7)
python/cube/github/pulls.py (2)
  • check_gh_installed (23-29)
  • fetch_pr (32-87)
python/cube/cli.py (2)
  • pr (461-507)
  • clean (573-581)
python/cube/core/decision_parser.py (1)
  • get_decision_file_path (57-76)
python/cube/core/user_config.py (1)
  • get_judge_configs (323-326)
python/cube/automation/stream.py (1)
  • add (19-38)
python/cube/automation/judge_panel.py (1)
  • launch_judge_panel (296-645)
python/cube/automation/ui_review_prompts.py (1)
  • build_ui_review_prompt (33-82)
🪛 LanguageTool
python/cube/automation/ui_review_refs/principles.md

[uncategorized] ~45-~45: Although a hyphen is possible, it is not necessary in a compound modifier in which the first word is an adverb that ends in ‘ly’.
Context: ...need a label; icon-only is reserved for universally-known actions. - Show constraints before subm...

(HYPHENATED_LY_ADVERB_ADJECTIVE)

python/cube/automation/ui_review_refs/interaction-psychology.md

[grammar] ~21-~21: It seems that the correct verb form here is “mean”.
Context: ...ith the number of choices. More options means slower decisions and higher abandonment...

(AGREEMENT_SENT_START_2)


[grammar] ~97-~97: Consider using “to” with “prefer”.
Context: ...tive recovery cost. Practical rules: - Prefer inline interactions over modals; prefer modals over page redirects. - I...

(PREFER_OVER_TO)


[grammar] ~97-~97: Consider using “to” with “prefer”.
Context: ...Prefer inline interactions over modals; prefer modals over page redirects. - If a sub-task can be compl...

(PREFER_OVER_TO)

python/cube/automation/ui_review_refs/icons.md

[grammar] ~7-~7: Consider using “to” with “prefer”.
Context: ... mix outlined/filled/3D/emoji styles. - Prefer obvious meanings over clever metaphors. If an icon can be misunderstood, add a...

(PREFER_OVER_TO)


[uncategorized] ~15-~15: Although a hyphen is possible, it is not necessary in a compound modifier in which the first word is an adverb that ends in ‘ly’.
Context: ...or text+icon; icon-only is reserved for universally-known actions. - Tooltips: tooltips are suppo...

(HYPHENATED_LY_ADVERB_ADJECTIVE)


[grammar] ~18-~18: Consider using “to” with “prefer”.
Context: ...rimary way to understand an action. ## Prefer Text Over Icons When - The action is uncommon in your ...

(PREFER_OVER_TO)

python/cube/automation/ui_review_refs/system-principles.md

[uncategorized] ~12-~12: Possible missing comma found.
Context: ...efinition: Each screen has one dominant objective with the highest visual and interaction...

(AI_HYDRA_LEO_MISSING_COMMA)

python/cube/automation/ui_review_refs/design-psych.md

[misspelling] ~29-~29: This word is normally spelled as one.
Context: ...ng to show what belongs together. - For multi-part objects, align actions with the part th...

(EN_COMPOUNDS_MULTI_PART)

python/cube/automation/ui_review_refs/checklists.md

[uncategorized] ~25-~25: Although a hyphen is possible, it is not necessary in a compound modifier in which the first word is an adverb that ends in ‘ly’.
Context: ...OK/Done). - Icon-only is reserved for universally-known actions (search/close/more/settings). -...

(HYPHENATED_LY_ADVERB_ADJECTIVE)


[grammar] ~30-~30: The verb form ‘hover’ does not seem to be suitable in this context.
Context: ... - Card/list rows that open should have hover + chevron/affordance cue or a clear "Vi...

(HAVE_VB)

🪛 Ruff (0.15.1)
python/cube/cli.py

[warning] 566-566: Do not catch blind exception: Exception

(BLE001)

python/cube/commands/ui_review.py

[warning] 71-71: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 77-80: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 253-253: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

python/cube/automation/ui_review_prompts.py

[warning] 34-34: Unused function argument: task_id

(ARG001)

🔇 Additional comments (23)
python/cube/automation/ui_review_refs/review-template.md (1)

1-59: Well-structured review template.

The template provides clear priority levels (P0/P1/P2) with consistent structure for each finding type. The diagnosis categories align well with the design psychology concepts defined in other reference documents. The verification checklist is comprehensive and actionable.

python/cube/automation/ui_review_refs/icons.md (1)

1-40: Clear and practical icon guidelines.

The document provides actionable rules for icon usage with sensible recommendations. The static analysis hints regarding "Prefer X over Y" phrasing and hyphenation are false positives — the current phrasing is idiomatic for guideline documents.

python/cube/automation/ui_review_refs/system-principles.md (1)

1-84: Comprehensive system-level principles.

The principles are well-defined with clear review questions. The "UI Copy Source Discipline" section (lines 15-29) is particularly valuable for ensuring AI-generated content doesn't leak implementation details into user-facing copy. The static analysis hint about a missing comma is a false positive.

python/cube/automation/ui_review_refs/interaction-psychology.md (1)

1-145: Solid HCI reference material.

The document provides a well-organised compact reference of established HCI principles with practical rules. The specific touch target sizes (44×44 CSS px for web, 48×48 dp for mobile) on line 14 are aligned with platform guidelines.

python/cube/automation/ui_review_refs/principles.md (1)

1-96: Comprehensive UI/UX principles document.

The document is well-structured with clear hierarchy. The "Anti-AI Self-Check" section (lines 89-96) is particularly valuable for catching common AI-generated UI issues like decorative gradients and emoji misuse. The concrete spacing values (4px base unit with defined scale) provide actionable guidance.

python/cube/automation/ui_review_refs/checklists.md (1)

1-101: Practical expanded checklists.

The document provides detailed, actionable checklists for specific UI patterns and page types. The Universal States section (lines 5-19) ensures comprehensive state coverage, and the Motion Checklist (lines 76-84) includes useful red flags for identifying anti-patterns.

python/cube/automation/ui_review_refs/design-psych.md (1)

1-89: Useful design psychology reference.

The document provides clear explanations of core design psychology concepts with practical rules for each. The concepts align with established design literature (Norman's principles). The static analysis hint about "multi-part" spelling is a false positive — hyphenated compound is acceptable here.

python/cube/automation/judge_panel.py (6)

237-241: LGTM — Clean handling of UI-review decision parsing.

The early return for ui-review review type correctly extracts P0/P1/P2 findings and returns a summary string without falling through to other review type logic.


317-320: LGTM — Correctly skips worktree prefetch for UI reviews.

UI reviews operate on UI artifacts rather than writer branches, so skipping the worktree prefetch is appropriate.


338-340: LGTM — Appropriate judge filtering for UI reviews.

Excluding cli-review type judges makes sense as they are code-specific tools not suited for UI/UX evaluation.


558-568: LGTM — Consistently skips branch operations for UI reviews.

This is consistent with the earlier worktree prefetch skip; UI reviews don't involve writer branches.


571-574: LGTM — Clear UI-review banner and guidance.

The banner follows the existing pattern for other review types and provides clear guidance on the expected output format (P0/P1/P2 findings).


366-369: No action needed. The function signature in ui_review_prompts.py is build_ui_review_judge_instructions(task_id: str) -> str:, which accepts only task_id as required. The import and call at lines 368–369 are correct and follow the lazy-import pattern used for other review types.

python/cube/automation/ui_review_prompts.py (3)

7-16: Reference list is tidy and easy to extend.

Centralising the filenames keeps the knowledge base manageable.


19-30: Ref loading and knowledge-base assembly look solid.

Gracefully skips missing refs and keeps separation clear.


85-144: Judge instruction block is clear and complete.

Nice, explicit schema and priority rules.

python/cube/cli.py (1)

22-22: UI review command import is wired correctly.

python/cube/commands/ui_review.py (6)

15-21: UI allowlist constants look sensible.


24-37: File concatenation helper is clean.


63-82: PR UI diff fetch flow is clear.


137-139: Markup stripping helper is fine for plain-text output.


142-213: Report rendering is consistent and readable.


215-331: End-to-end UI review orchestration looks coherent.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@python/cube/cli.py`:
- Around line 526-569: The ui_review() command currently catches all Exceptions
which swallows and reprints typer.Exit raised by ui_review_command; update the
exception handling so typer.Exit is allowed to bubble: add an explicit except
typer.Exit: raise block before the generic except Exception so ui_review_command
can control its own exit behavior, and keep the existing except Exception to
call _print_error(e) and then import sys; sys.exit(1). Reference: function
ui_review, call to ui_review_command, helper _print_error, and typer.Exit.

In `@python/cube/commands/ui_review.py`:
- Around line 40-60: The diff filtering currently uses parts[-1].lstrip("b/")
which strips any leading 'b' or '/' characters and can mangle paths; in
_filter_ui_diff replace that with exact prefix handling (e.g., check if
parts[-1].startswith("b/") and then remove the first two characters) so filepath
becomes the true path before computing suffix and filename for the
UI_FILE_EXTENSIONS and UI_CONFIG_FILENAMES checks; ensure you only remove the
exact "b/" prefix and leave other leading characters intact.

---

Nitpick comments:
In `@python/cube/automation/ui_review_prompts.py`:
- Around line 33-82: The function build_ui_review_prompt currently has an unused
parameter task_id (causing ARG001); either remove task_id from the signature and
all callers, or surface it in the generated prompt for traceability by inserting
a short "Review ID: {task_id}" (or similar) near the top of the returned prompt
string (e.g., directly after "# UI Review") so the parameter is used; update any
callers if you remove the parameter.

In `@python/cube/commands/ui_review.py`:
- Around line 85-134: _collect_findings currently iterates all judge configs and
prints "No decision file" warnings for cli-review judges that aren't part of UI
runs; filter out CLI-review judges early to avoid noisy warnings by skipping any
jconfig whose key indicates the CLI reviewer (e.g., if jconfig.key ==
"cli-review") before computing get_decision_file_path and processing files—add a
guard in the loop inside _collect_findings to continue for CLI-review configs so
only UI-related judges are checked.

Comment on lines +526 to +569
@app.command(name="ui-review")
def ui_review(
file: Annotated[
Optional[list[str]], typer.Option("--file", "-f", help="UI file(s) to review (repeatable)")
] = None,
description: Annotated[
Optional[str], typer.Option("--description", "-d", help="Free-text description of the UI to review")
] = None,
context: Annotated[
str, typer.Option("--context", "-c", help="Platform, target user, and primary task (one-liner)")
] = "",
pr: Annotated[
Optional[int], typer.Option("--pr", help="GitHub PR number — review UI-related file changes only")
] = None,
output: Annotated[
Optional[str], typer.Option("--output", "-o", help="Save report to this file path")
] = None,
fresh: Annotated[
bool, typer.Option("--fresh", help="Start fresh judge sessions (ignore cached sessions)")
] = False,
):
"""Review UI/UX quality using the judge panel.

Examples:
cube ui-review --file web-ui/src/pages/Dashboard.tsx
cube ui-review --file web-ui/src/pages/Dashboard.tsx --file web-ui/src/pages/TaskDetail.tsx
cube ui-review --pr 123
cube ui-review --description "Settings page with 15 ungrouped toggles"
cube ui-review --file src/App.tsx --context "web dashboard, developer users, monitor agent tasks"
cube ui-review --file src/App.tsx --output reviews/dashboard.md
"""
try:
ui_review_command(
files=list(file) if file else [],
description=description,
context=context,
pr=pr,
output=output,
fresh=fresh,
)
except Exception as e:
_print_error(e)
import sys
sys.exit(1)
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Let typer.Exit bubble to avoid double error output.

Line 557-567: ui_review_command already prints validation errors and raises typer.Exit; catching Exception here reprints and normalises the exit. Catch typer.Exit separately.

✅ Safer error handling
     try:
         ui_review_command(
             files=list(file) if file else [],
             description=description,
             context=context,
             pr=pr,
             output=output,
             fresh=fresh,
         )
-    except Exception as e:
+    except typer.Exit:
+        raise
+    except Exception as e:
         _print_error(e)
         import sys
         sys.exit(1)
🧰 Tools
🪛 Ruff (0.15.1)

[warning] 566-566: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/cli.py` around lines 526 - 569, The ui_review() command currently
catches all Exceptions which swallows and reprints typer.Exit raised by
ui_review_command; update the exception handling so typer.Exit is allowed to
bubble: add an explicit except typer.Exit: raise block before the generic except
Exception so ui_review_command can control its own exit behavior, and keep the
existing except Exception to call _print_error(e) and then import sys;
sys.exit(1). Reference: function ui_review, call to ui_review_command, helper
_print_error, and typer.Exit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — will let typer.Exit bubble up directly and only catch unexpected exceptions separately.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roy-songzhe-li, sounds good! Just a reminder that the fix should be applied to both ui_review command definitions in cli.py, since the AI summary noted a duplicate registration of @app.command(name="ui-review") was introduced. The corrected pattern for each:

    try:
        ui_review_command(
            files=list(file) if file else [],
            description=description,
            context=context,
            pr=pr,
            repo=repo,
            output=output,
            post=post,
            fresh=fresh,
        )
    except typer.Exit:
        raise
    except Exception as e:
        _print_error(e)
        sys.exit(1)

Also, the duplicate command definition itself should be resolved — Typer will raise an error at startup if the same command name is registered twice, so one of the two @app.command(name="ui-review") blocks should be removed.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
python/cube/cli.py (1)

564-578: ⚠️ Potential issue | 🟡 Minor

Let typer.Exit bubble to avoid double error output.

Line 575 catches all exceptions and reprints, which also intercepts typer.Exit raised by ui_review_command (it already prints validation errors). Re-raise typer.Exit first.

#!/bin/bash
# Locate typer.Exit usage to confirm it is raised in command validation paths.
rg -n --type=py 'typer\.Exit'
✅ Proposed fix
     try:
         ui_review_command(
             files=list(file) if file else [],
             description=description,
             context=context,
             pr=pr,
             repo=repo,
             output=output,
             post=post,
             fresh=fresh,
         )
+    except typer.Exit:
+        raise
     except Exception as e:
         _print_error(e)
         import sys
         sys.exit(1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/cli.py` around lines 564 - 578, The except block around the
ui_review_command call is catching all exceptions including typer.Exit and
causing duplicate error output; update the handler so that if the exception is
an instance of typer.Exit it is re-raised (allowing typer's own output/exit
behavior), otherwise call _print_error(e) and exit as before—refer to
ui_review_command and _print_error and check for typer.Exit before handling
other exceptions.
python/cube/commands/ui_review.py (1)

46-55: ⚠️ Potential issue | 🟡 Minor

Avoid lstrip("b/") path mangling.

Line 52 uses lstrip("b/"), which strips any leading b or / characters (e.g., b/b.css becomes .css), breaking the suffix check. Strip the exact "b/" prefix instead.

#!/bin/bash
# Demonstrate str.lstrip behaviour with a "b/" prefix.
python - <<'PY'
print("b/b.css".lstrip("b/"))  # Expected output: .css
PY
🔧 Proposed fix
-                filepath = parts[-1].lstrip("b/")
+                filepath = parts[-1]
+                if filepath.startswith("b/"):
+                    filepath = filepath[2:]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/commands/ui_review.py` around lines 46 - 55, The code in the loop
that computes filepath uses filepath = parts[-1].lstrip("b/"), which incorrectly
strips any leading 'b' or '/' characters and mangles paths (affecting suffix and
filename checks). Change that to remove only the exact "b/" prefix (e.g. use
filepath = filepath.removeprefix("b/") or if filepath.startswith("b/"): filepath
= filepath[2:]) before computing suffix and filename so UI_FILE_EXTENSIONS and
UI_CONFIG_FILENAMES checks work correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@python/cube/commands/ui_review.py`:
- Around line 271-274: The code writes report to output_path without ensuring
its parent directory exists, which fails when the target is in a non-existent
folder; before calling output_path.write_text(clean) in the output_path handling
block, create the parent directory for output_path (use output_path.parent.mkdir
with parents=True and exist_ok=True or equivalent) so the directory is created
if missing, then proceed to write_text and print_success as before (refer to
symbols output_path, write_text, output_path.parent, _strip_markup,
print_success).
- Around line 317-327: The PR task_id currently set as task_id =
f"ui-review-pr-{pr}" can collide across repos; update the task_id generation in
the PR branch (where pr is not None, near _fetch_pr_ui_diff) to include a repo
slug when repo is provided (e.g., use the repo string or repo identifier,
sanitize slashes into dashes) so the id becomes something like
"ui-review-pr-{repo_slug}-{pr}"; ensure the same sanitized slug logic is applied
wherever task_id is later used for prompt/decision file names.

---

Duplicate comments:
In `@python/cube/cli.py`:
- Around line 564-578: The except block around the ui_review_command call is
catching all exceptions including typer.Exit and causing duplicate error output;
update the handler so that if the exception is an instance of typer.Exit it is
re-raised (allowing typer's own output/exit behavior), otherwise call
_print_error(e) and exit as before—refer to ui_review_command and _print_error
and check for typer.Exit before handling other exceptions.

In `@python/cube/commands/ui_review.py`:
- Around line 46-55: The code in the loop that computes filepath uses filepath =
parts[-1].lstrip("b/"), which incorrectly strips any leading 'b' or '/'
characters and mangles paths (affecting suffix and filename checks). Change that
to remove only the exact "b/" prefix (e.g. use filepath =
filepath.removeprefix("b/") or if filepath.startswith("b/"): filepath =
filepath[2:]) before computing suffix and filename so UI_FILE_EXTENSIONS and
UI_CONFIG_FILENAMES checks work correctly.
ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 532e962 and 57c1e78.

📒 Files selected for processing (4)
  • python/cube/cli.py
  • python/cube/commands/ui_review.py
  • python/cube/github/pulls.py
  • python/cube/github/reviews.py
📜 Review details
🧰 Additional context used
🪛 Ruff (0.15.1)
python/cube/github/reviews.py

[error] 221-221: subprocess call: check for execution of untrusted input

(S603)


[warning] 223-223: Avoid specifying long messages outside the exception class

(TRY003)

python/cube/cli.py

[warning] 575-575: Do not catch blind exception: Exception

(BLE001)

python/cube/github/pulls.py

[error] 55-55: subprocess call: check for execution of untrusted input

(S603)


[warning] 56-57: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)


[warning] 74-74: Consider ["gh", "pr", "diff", str(pr_number), *repo_flags] instead of concatenation

Replace with ["gh", "pr", "diff", str(pr_number), *repo_flags]

(RUF005)

python/cube/commands/ui_review.py

[warning] 76-76: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 82-85: Avoid specifying long messages outside the exception class

(TRY003)


[error] 198-198: f-string without any placeholders

Remove extraneous f prefix

(F541)


[warning] 323-323: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🔇 Additional comments (2)
python/cube/github/pulls.py (1)

32-75: Repo-scoped PR fetch looks solid.

The optional repo handling is threaded consistently without changing default behaviour.

python/cube/github/reviews.py (1)

201-224: post_pr_comment wiring looks good.

The helper is straightforward and fits the existing CLI posting pattern.

…ad_sha

- pulls.py: add repo param to fetch_pr; fix head_sha to use full SHA (was [:7], caused 422 on GitHub review API)
- ui_review_prompts.py: add path/line fields to P0/P1 decision format so judges can reference specific diff lines
- ui_review.py: add --repo for cross-repo PRs; add --post to publish as GitHub review with inline comments; fallback to plain comment on API error
- cli.py: expose --repo, --post flags
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (4)
python/cube/commands/ui_review.py (3)

265-335: ⚠️ Potential issue | 🟠 Major

Create the output directory before writing the report.

Line 332–334 will fail if --output targets a non-existent folder (e.g., reviews/report.md).

🛠️ Proposed fix
     if output_path:
+        output_path.parent.mkdir(parents=True, exist_ok=True)
         clean = "\n".join(_strip_markup(line) for line in lines)
         output_path.write_text(clean)
         print_success(f"Report saved to {output_path}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/commands/ui_review.py` around lines 265 - 335, The function
_display_findings writes the cleaned report to output_path without ensuring the
parent directory exists, which will raise if the target folder is missing;
before calling output_path.write_text(clean) in _display_findings, create the
directory with output_path.parent.mkdir(parents=True, exist_ok=True) (and only
do this when output_path.parent is not empty) so the file can be written safely,
then proceed to write and call print_success as before.

338-474: ⚠️ Potential issue | 🟠 Major

Include repo slug in PR task IDs to avoid collisions.

Line 389 uses only the PR number, so prompts/decisions can clash across repos with the same PR ID.

🧭 Suggested fix
-        task_id = f"ui-review-pr-{pr}"
+        repo_slug = (repo or "local").replace("/", "-")
+        task_id = f"ui-review-pr-{repo_slug}-{pr}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/commands/ui_review.py` around lines 338 - 474, The PR-only branch
of ui_review_command builds task_id using only the PR number (task_id =
f"ui-review-pr-{pr}"), which can collide across repos; update that branch to
include the repo slug (use the provided repo parameter, normalizing "/" to "-"
or falling back to a short local tag) when constructing task_id (e.g.,
incorporate repo into task_id), and ensure any related uses (prompt_path naming,
launch_judge_panel call, and _collect_findings/_post_inline_review invocations
that use task_id or head_sha) continue to reference the new task_id format; key
symbols: ui_review_command, task_id, _fetch_pr_ui_diff, launch_judge_panel,
_collect_findings, _post_inline_review.

40-60: ⚠️ Potential issue | 🟡 Minor

Use exact b/ prefix stripping to avoid path mangling.

Line 52 uses lstrip("b/"), which removes any leading b or / characters and can corrupt paths like b/b.css. Strip the exact prefix instead.

🛠️ Safer prefix handling
-                filepath = parts[-1].lstrip("b/")
+                filepath = parts[-1]
+                if filepath.startswith("b/"):
+                    filepath = filepath[2:]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/commands/ui_review.py` around lines 40 - 60, The code in
_filter_ui_diff uses filepath = parts[-1].lstrip("b/") which can remove any
leading 'b' or '/' characters and corrupt paths (e.g., b/b.css); change this to
strip the exact "b/" prefix instead by checking if parts[-1] starts with "b/"
and then slicing off the first two characters (update the variable filepath
accordingly) before computing suffix/name so Path(filepath).suffix and
Path(filepath).name work correctly; ensure this exact-prefix logic replaces the
lstrip call.
python/cube/cli.py (1)

526-577: ⚠️ Potential issue | 🟡 Minor

Let typer.Exit bubble to avoid double error output.

Line 563–577 catches all Exception, which intercepts typer.Exit and reprints errors.

✅ Safer error handling
     try:
         ui_review_command(
             files=list(file) if file else [],
             description=description,
             context=context,
             pr=pr,
             repo=repo,
             output=output,
             post=post,
             fresh=fresh,
         )
-    except Exception as e:
+    except typer.Exit:
+        raise
+    except Exception as e:
         _print_error(e)
         import sys
         sys.exit(1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/cli.py` around lines 526 - 577, The current ui_review wrapper
catches all Exception and therefore intercepts typer.Exit, causing duplicate
error output; update the exception handling in the ui_review function so
typer.Exit is allowed to propagate: either add an explicit except typer.Exit:
raise before the generic except, or in the generic except check isinstance(e,
typer.Exit) and re-raise; keep the existing _print_error(e) and sys.exit(1)
behavior only for non-typer.Exit exceptions. Ensure you reference the ui_review
function and its call to ui_review_command and preserve existing cleanup/logging
for other exceptions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@python/cube/cli.py`:
- Around line 526-577: The current ui_review wrapper catches all Exception and
therefore intercepts typer.Exit, causing duplicate error output; update the
exception handling in the ui_review function so typer.Exit is allowed to
propagate: either add an explicit except typer.Exit: raise before the generic
except, or in the generic except check isinstance(e, typer.Exit) and re-raise;
keep the existing _print_error(e) and sys.exit(1) behavior only for
non-typer.Exit exceptions. Ensure you reference the ui_review function and its
call to ui_review_command and preserve existing cleanup/logging for other
exceptions.

In `@python/cube/commands/ui_review.py`:
- Around line 265-335: The function _display_findings writes the cleaned report
to output_path without ensuring the parent directory exists, which will raise if
the target folder is missing; before calling output_path.write_text(clean) in
_display_findings, create the directory with
output_path.parent.mkdir(parents=True, exist_ok=True) (and only do this when
output_path.parent is not empty) so the file can be written safely, then proceed
to write and call print_success as before.
- Around line 338-474: The PR-only branch of ui_review_command builds task_id
using only the PR number (task_id = f"ui-review-pr-{pr}"), which can collide
across repos; update that branch to include the repo slug (use the provided repo
parameter, normalizing "/" to "-" or falling back to a short local tag) when
constructing task_id (e.g., incorporate repo into task_id), and ensure any
related uses (prompt_path naming, launch_judge_panel call, and
_collect_findings/_post_inline_review invocations that use task_id or head_sha)
continue to reference the new task_id format; key symbols: ui_review_command,
task_id, _fetch_pr_ui_diff, launch_judge_panel, _collect_findings,
_post_inline_review.
- Around line 40-60: The code in _filter_ui_diff uses filepath =
parts[-1].lstrip("b/") which can remove any leading 'b' or '/' characters and
corrupt paths (e.g., b/b.css); change this to strip the exact "b/" prefix
instead by checking if parts[-1] starts with "b/" and then slicing off the first
two characters (update the variable filepath accordingly) before computing
suffix/name so Path(filepath).suffix and Path(filepath).name work correctly;
ensure this exact-prefix logic replaces the lstrip call.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57c1e78 and baad64e.

📒 Files selected for processing (4)
  • python/cube/automation/ui_review_prompts.py
  • python/cube/cli.py
  • python/cube/commands/ui_review.py
  • python/cube/github/pulls.py
📜 Review details
🧰 Additional context used
🧬 Code graph analysis (1)
python/cube/cli.py (1)
python/cube/commands/ui_review.py (1)
  • ui_review_command (338-474)
🪛 Ruff (0.15.2)
python/cube/cli.py

[warning] 574-574: Do not catch blind exception: Exception

(BLE001)

python/cube/commands/ui_review.py

[warning] 71-71: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 77-80: Avoid specifying long messages outside the exception class

(TRY003)


[error] 223-223: subprocess call: check for execution of untrusted input

(S603)


[error] 224-224: Starting a process with a partial executable path

(S607)


[error] 233-233: subprocess call: check for execution of untrusted input

(S603)


[warning] 241-241: Avoid specifying long messages outside the exception class

(TRY003)


[error] 252-252: Starting a process with a partial executable path

(S607)


[warning] 386-386: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

python/cube/automation/ui_review_prompts.py

[warning] 34-34: Unused function argument: task_id

(ARG001)

python/cube/github/pulls.py

[error] 54-54: subprocess call: check for execution of untrusted input

(S603)


[warning] 55-56: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)


[warning] 73-73: Consider ["gh", "pr", "diff", str(pr_number), *repo_flags] instead of concatenation

Replace with ["gh", "pr", "diff", str(pr_number), *repo_flags]

(RUF005)

🔇 Additional comments (10)
python/cube/github/pulls.py (1)

32-90: Solid repo-scoped PR fetch update.

Line 32–90 cleanly threads the optional repo into both metadata and diff calls, and keeping the full head SHA improves downstream accuracy.

python/cube/automation/ui_review_prompts.py (3)

7-31: Reference loading looks robust.

Skipping missing files and joining non-empty content is a clean, safe approach.


33-82: Prompt composition is clear and well-structured.

Context handling and input labelling are straightforward and easy to follow.


85-153: Judge instruction block is crisp and enforceable.

The JSON schema and path/line rules are explicit and actionable.

python/cube/commands/ui_review.py (6)

24-37: File ingestion is straightforward and resilient.

Missing files and read failures are handled cleanly without aborting the run.


63-82: UI-diff filtering is nicely gated.

Clear error messaging when no UI files change makes the UX solid.


85-134: Findings aggregation reads well.

The per-judge tagging and quick-win de-duplication are well-implemented.


137-245: Inline review assembly looks correct.

The summary + inline split and fallback path are handled cleanly.


247-257: Repo resolution helper is concise.

Good to keep the CLI call small and return a safe empty string on failure.


260-262: Markup stripping helper is fine.

Simple and effective for Rich tags.

…ud mode

cursor-agent without --print tries to connect to local Cursor IDE server
on 127.0.0.1:7897 which may not be running. With --print and --trust,
cursor-agent connects directly to Cursor's cloud API.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@python/cube/core/adapters/cursor.py`:
- Line 33: The command array assigned to cmd in
python/cube/core/adapters/cursor.py includes an invalid --trust flag which
causes cursor-agent to fail; remove the --trust element from the cmd list (the
array building the invocation of cursor-agent, e.g., the cmd variable created
where the command is constructed) so the resulting invocation matches the
documented pattern (e.g., cursor-agent --print --force --output-format
stream-json --stream-partial-output --model <model> "prompt") and ensure any
tests or callers still pass the prompt argument in the correct position.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between baad64e and 731f58b.

📒 Files selected for processing (1)
  • python/cube/core/adapters/cursor.py

Copy link
Copy Markdown
Contributor

@jacsamell jacsamell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Good concept and clean structure. A few issues to address:


1. cursor.py--trust and --print added globally (biggest risk)

python/cube/core/adapters/cursor.py--trust bypasses permission prompts for all agents (writers, judges, prompters), not just UI review. This was added to make UI review work but affects every flow. Please scope it or confirm these flags are intentionally global.


2. pulls.py — SHA truncation removed, affects all callers

python/cube/github/pulls.pyhead_sha now returns the full 40-char SHA instead of a 7-char short SHA. Other code that displays or uses head_sha may not expect this. Either revert here and truncate only in ui_review.py where the full SHA is needed for the GitHub API.


3. ui_review.py — No deduplication of P0/P1/P2 findings across judges

If 3 judges flag the same issue it appears 3 times. quick_wins is deduplicated but findings are not. Group by finding and list contributing judges — same pattern as the pr-review deduper.


4. ui_review.py — Timestamp task ID = never resumable for file/description input

task_id = f"ui-review-{datetime.now().strftime('%Y%m%d-%H%M%S')}" — every run creates new decision files. If a judge is interrupted you start over. For --pr it's stable, for --file/--description it isn't. Consider a hash of the input instead.


5. ui_review.py--post uses emoji in GitHub comments

The reference docs in this PR explicitly say "No emoji as icons or UI decoration." The --post output uses 🔴 🟡 ⚪. Swap for text: P0 — Blocker, P1 — Important, P2 — Polish.


6. ui_review_prompts.py — Full KB (~600+ lines) embedded in every judge prompt

No caching. Expensive and may push task content out of context for smaller models. Consider passing the KB once or trimming by input type.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/cube/github/pulls.py (1)

54-77: ⚠️ Potential issue | 🟠 Major

Add timeouts for gh subprocess calls in fetch_pr.

Line 54 and Line 72 call external network-backed commands without timeout, so the command can hang indefinitely and stall the whole review flow.

⏱️ Suggested fix
-    result = subprocess.run(
-        ["gh", "pr", "view", str(pr_number), "--json", "number,title,body,headRefName,baseRefName,headRefOid,reviews"]
-        + repo_flags,
-        capture_output=True,
-        text=True,
-        cwd=cwd,
-    )
+    try:
+        result = subprocess.run(
+            [
+                "gh",
+                "pr",
+                "view",
+                str(pr_number),
+                "--json",
+                "number,title,body,headRefName,baseRefName,headRefOid,reviews",
+                *repo_flags,
+            ],
+            capture_output=True,
+            text=True,
+            cwd=cwd,
+            timeout=30,
+        )
+    except subprocess.TimeoutExpired as e:
+        raise RuntimeError(f"Timed out fetching PR #{pr_number} metadata") from e
@@
-    diff_result = subprocess.run(
-        ["gh", "pr", "diff", str(pr_number)] + repo_flags,
-        capture_output=True,
-        text=True,
-        cwd=cwd,
-    )
+    try:
+        diff_result = subprocess.run(
+            ["gh", "pr", "diff", str(pr_number), *repo_flags],
+            capture_output=True,
+            text=True,
+            cwd=cwd,
+            timeout=45,
+        )
+    except subprocess.TimeoutExpired as e:
+        raise RuntimeError(f"Timed out fetching PR #{pr_number} diff") from e
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/github/pulls.py` around lines 54 - 77, The subprocess.run calls
in fetch_pr (the calls that set result and diff_result for "gh pr view" and "gh
pr diff") lack timeouts and can hang; add a timeout argument (e.g. timeout=30 or
a configurable parameter) to both subprocess.run invocations and catch
subprocess.TimeoutExpired around those calls to raise a clear RuntimeError
(include which PR and that the gh command timed out). Ensure you update both the
"gh pr view" call that assigns result and the "gh pr diff" call that assigns
diff_result and handle the TimeoutExpired exception to avoid stalling the review
flow.
♻️ Duplicate comments (1)
python/cube/cli.py (1)

566-580: ⚠️ Potential issue | 🟡 Minor

Let typer.Exit bubble in ui_review.

Line 577 catches Exception, which also captures typer.Exit raised by ui_review_command and can reprint/normalise controlled exits.

✅ Suggested fix
     try:
         ui_review_command(
             files=list(file) if file else [],
             description=description,
             context=context,
             pr=pr,
             repo=repo,
             output=output,
             post=post,
             fresh=fresh,
         )
+    except typer.Exit:
+        raise
     except Exception as e:
         _print_error(e)
         import sys
         sys.exit(1)
#!/bin/bash
# Verify root cause by checking:
# 1) ui_review_command raises typer.Exit
# 2) ui_review currently catches broad Exception without a typer.Exit passthrough
rg -n -C2 'raise typer\.Exit|def ui_review\(|except typer\.Exit|except Exception as e' \
  python/cube/commands/ui_review.py \
  python/cube/cli.py
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cube/cli.py` around lines 566 - 580, The try/except in the CLI is
catching typer.Exit raised by ui_review_command; update the exception handling
so typer.Exit is allowed to propagate: in the try block around
ui_review_command, add an explicit except typer.Exit: raise (or re-raise) before
the broad except Exception that calls _print_error and sys.exit(1), ensuring
ui_review_command, ui_review_command(...) and _print_error remain the referenced
symbols to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@python/cube/github/pulls.py`:
- Around line 54-77: The subprocess.run calls in fetch_pr (the calls that set
result and diff_result for "gh pr view" and "gh pr diff") lack timeouts and can
hang; add a timeout argument (e.g. timeout=30 or a configurable parameter) to
both subprocess.run invocations and catch subprocess.TimeoutExpired around those
calls to raise a clear RuntimeError (include which PR and that the gh command
timed out). Ensure you update both the "gh pr view" call that assigns result and
the "gh pr diff" call that assigns diff_result and handle the TimeoutExpired
exception to avoid stalling the review flow.

---

Duplicate comments:
In `@python/cube/cli.py`:
- Around line 566-580: The try/except in the CLI is catching typer.Exit raised
by ui_review_command; update the exception handling so typer.Exit is allowed to
propagate: in the try block around ui_review_command, add an explicit except
typer.Exit: raise (or re-raise) before the broad except Exception that calls
_print_error and sys.exit(1), ensuring ui_review_command, ui_review_command(...)
and _print_error remain the referenced symbols to locate the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c323679-331c-4a47-92ee-5f95d43e0bdb

📥 Commits

Reviewing files that changed from the base of the PR and between 731f58b and 78f87d8.

📒 Files selected for processing (2)
  • python/cube/cli.py
  • python/cube/github/pulls.py
📜 Review details
🧰 Additional context used
🧬 Code graph analysis (1)
python/cube/cli.py (1)
python/cube/commands/ui_review.py (1)
  • ui_review_command (338-474)
🪛 Ruff (0.15.4)
python/cube/github/pulls.py

[error] 54-54: subprocess call: check for execution of untrusted input

(S603)


[warning] 55-56: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)


[warning] 73-73: Consider ["gh", "pr", "diff", str(pr_number), *repo_flags] instead of concatenation

Replace with ["gh", "pr", "diff", str(pr_number), *repo_flags]

(RUF005)

python/cube/cli.py

[warning] 577-577: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (2)
python/cube/github/pulls.py (1)

32-39: Repo-scoped PR fetch and full head SHA propagation look good.

Line 51, Line 55, and Line 73 consistently apply --repo scoping, and Line 89 correctly preserves the full headRefOid for downstream commit-accurate operations.

Also applies to: 51-57, 73-73, 89-90

python/cube/cli.py (1)

22-23: ui-review CLI wiring is clean and matches the command contract.

Options are passed through consistently (--pr, --repo, --post, --output, --fresh), which keeps the CLI surface aligned with ui_review_command.

Also applies to: 529-575

@roy-songzhe-li
Copy link
Copy Markdown
Contributor Author

@jacsamell Thanks for the detailed review! All 6 points addressed:

1. cursor.py--trust removed

Removed --trust from the global command. It was incorrectly scoped — it bypassed permission prompts for all agents, not just UI review. --print is kept since it's needed for headless/cloud mode across all flows.

2. pulls.py — SHA truncation restored

Reverted head_sha back to returning the 7-char short SHA ([:7]) in PRData. In ui_review.py, the full 40-char SHA is now fetched via a separate gh pr view --json headRefOid call — only the GitHub review API needs it, so no reason to change the shared data layer.

3. ui_review.py — Findings now deduplicated across judges

Added _dedup_findings() that groups findings by normalized problem text. When multiple judges flag the same issue, it merges into one entry with all contributing judges listed (e.g. Judge A, Judge B). Same pattern as the pr-review comment_deduper.py.

4. ui_review.py — Stable task_id via content hash

Replaced datetime.now().strftime(...) with hashlib.sha256(content).hexdigest()[:10]. Task IDs now look like ui-review-file-a1b2c3d4e5 — deterministic and resumable. Same input always maps to the same task ID, so interrupted judges can resume.

5. ui_review.py — Emoji removed from --post output

Replaced 🔴 🟡 ⚪ 🎨 ⚡ with plain text: P0 — Blockers, P1 — Important, P2 — Polish, Quick Wins. Aligns with the reference docs.

6. ui_review_prompts.py — KB trimmed and cached

  • Added functools.lru_cache on _load_ref() to avoid redundant disk I/O
  • Split KB into _CORE_REFS (principles, system-principles, checklists, icons) and _EXTENDED_REFS (design-psych, interaction-psychology)
  • For pr_diff input type, only core refs are included (~320 lines vs ~614), saving ~234 lines of context per judge prompt

@jacsamell jacsamell merged commit 0acb046 into main Mar 10, 2026
@jacsamell jacsamell deleted the feature/ui-review branch March 10, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants