Skip to content

Add workspace hover cards with GitHub PR status#233

Merged
saddlepaddle merged 7 commits intomainfrom
silver-glacier-88
Dec 6, 2025
Merged

Add workspace hover cards with GitHub PR status#233
saddlepaddle merged 7 commits intomainfrom
silver-glacier-88

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Dec 2, 2025

Summary

  • Add hover cards to workspace tabs that display worktree info and GitHub PR status
  • Fetch PR data via gh CLI with graceful fallback when unavailable
  • Show worktree name, age, rebase warning, PR details (number, status, title, diff stats)
  • Add expandable checks list showing individual CI job statuses with clickable links
  • Display review status and "View on GitHub" button with GitHub logo

Test plan

  • Hover over a workspace tab to see the hover card
  • Verify worktree name and age display correctly
  • Verify PR info shows for branches with open PRs
  • Verify "No PR for this branch" shows for branches without PRs
  • Click "Show checks" to expand the checks list
  • Click individual checks to open them in GitHub
  • Click "View on GitHub" button to open the PR

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Workspace hover now shows GitHub PR info: badge, number, title, review status, checks summary, per-check list, additions/deletions, and a GitHub link.
    • Real-time GitHub PR status fetching via CLI with 30s cache and new endpoints to fetch workspace/worktree info.
    • Hover card integration for workspace context menu and propagation of workspace identifier.
  • Schema

    • Extended GitHub status schema with per-check items, diff stats, review decision, and overall checks status.
  • Documentation

    • Dashboard docs reorganized into a more modular structure.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown

vercel Bot commented Dec 2, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
website Ready Ready Preview Comment Dec 6, 2025 6:38pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 2, 2025

Warning

Rate limit exceeded

@saddlepaddle has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 15 minutes and 53 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 932eb25 and 763aff7.

📒 Files selected for processing (3)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github/github.ts (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github/index.ts (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github/types.ts (1 hunks)

Walkthrough

Adds a GitHub PR status fetcher using the gh CLI with 30s caching, exposes two TRPC endpoints to surface worktree and GitHub info, extends DB schema with check items and PR fields, and introduces a hover-card UI (plus components) to display PR details and checks for workspace tabs.

Changes

Cohort / File(s) Summary
GitHub CLI utility
apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
New module providing `fetchGitHubPRStatus(worktreePath: string): Promise<GitHubStatus
TRPC router endpoints
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
Adds getGitHubStatus and getWorktreeInfo public procedures; resolve workspace→worktree, call fetchGitHubPRStatus, update worktree.githubStatus when available, and return worktree metadata with fresh GitHub PR status or null.
Schema / Types
apps/desktop/src/main/lib/db/schemas.ts
Adds exported CheckItem type and extends GitHubStatus.pr with additions, deletions, reviewDecision, checksStatus, and checks: CheckItem[].
Hover card UI & content
apps/desktop/src/renderer/.../WorkspaceHoverCard/WorkspaceHoverCard.tsx
New WorkspaceHoverCardContent component that queries TRPC for worktree info and GitHub status, and renders PR header, PRStatusBadge, diff stats, title, checks summary/list (expandable), review status, GitHub link, and loading/no-PR states.
Hover card exports
apps/desktop/src/renderer/.../WorkspaceHoverCard/index.ts
Re-exports WorkspaceHoverCardContent.
Workspace item prop plumbing
apps/desktop/src/renderer/.../WorkspaceItem.tsx
Passes workspaceId={id} into WorkspaceItemContextMenu to enable workspace-specific hover content.
Context menu → HoverCard integration
apps/desktop/src/renderer/.../WorkspaceItemContextMenu.tsx
Wraps the existing context menu in a HoverCard, adds workspaceId prop, hover delays, and renders WorkspaceHoverCardContent inside HoverCardContent.
Checks list & item components
apps/desktop/src/renderer/.../ChecksList/*, .../CheckItemRow/*
New ChecksList collapsible component and CheckItemRow component with status icons and optional links; index barrels added for exports.
Checks summary & badges
apps/desktop/src/renderer/.../ChecksSummary/*, .../PRStatusBadge/*, .../ReviewStatus/*
New ChecksSummary, PRStatusBadge, and ReviewStatus components with corresponding index re-exports.
Docs & deps
AGENTS.md, apps/desktop/package.json
Documentation reorganization noted and new dependency date-fns: "^4.1.0" added to apps/desktop/package.json.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant UI as Workspace Tab
    participant Hover as HoverCard
    participant TRPC as Workspaces Router
    participant GHUtil as GitHub Utility
    participant GHCli as gh/git CLI

    User->>UI: hover over workspace tab
    UI->>Hover: open hovercard
    Hover->>TRPC: getWorktreeInfo(workspaceId)
    TRPC->>GHUtil: fetchGitHubPRStatus(worktreePath)
    GHUtil->>GHCli: git rev-parse HEAD (get branch/ref)
    GHCli-->>GHUtil: ref/hash
    GHUtil->>GHCli: gh repo view --json url
    GHCli-->>GHUtil: repo URL JSON
    GHUtil->>GHCli: gh pr view <ref> --json <fields>
    GHCli-->>GHUtil: PR + checks JSON
    GHUtil->>GHUtil: validate/parse/map checks, compute checksStatus
    GHUtil-->>TRPC: GitHubStatus | null
    TRPC-->>Hover: worktreeInfo + githubStatus
    Hover->>User: render PR details, badges, checks, and links
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–60 minutes

  • Inspect apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts for robust CLI invocation, timeouts, JSON validation, and error classification.
  • Validate DB schema changes in apps/desktop/src/main/lib/db/schemas.ts for compatibility and downstream usages.
  • Review WorkspaceHoverCard.tsx and related components for loading/fallback behavior, performance, and accessibility.
  • Confirm prop propagation and hover timing in WorkspaceItemContextMenu.tsx.

Possibly related PRs

Poem

🐇 I nibbled at branches under moonlit light,
The gh CLI whispered PRs into sight.
Hovering tabs, badges and checks in a row,
Little rabbit hops — code and green checks glow. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding hover cards to workspace tabs that display GitHub PR status information.
Description check ✅ Passed The description covers key sections from the template including a clear summary of changes, a comprehensive test plan, and additional context about the implementation and generated tooling.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (8)
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (2)

500-531: Consider adding request deduplication or throttling for frequent hovers.

The endpoint always fetches fresh data on every hover. If users rapidly hover over workspace tabs (e.g., moving mouse across many tabs), this could trigger many concurrent gh CLI calls. Consider:

  1. Using tRPC's built-in deduplication via staleTime on the client side
  2. Adding a short cache TTL check before fetching (e.g., skip fetch if last refresh was < 30s ago)

Example server-side throttle:

 		getGitHubStatus: publicProcedure
 			.input(z.object({ workspaceId: z.string() }))
 			.query(async ({ input }) => {
 				const workspace = db.data.workspaces.find(
 					(w) => w.id === input.workspaceId,
 				);
 				if (!workspace) {
 					return null;
 				}

 				const worktree = db.data.worktrees.find(
 					(wt) => wt.id === workspace.worktreeId,
 				);
 				if (!worktree) {
 					return null;
 				}

+				// Return cached data if recently fetched (< 30s ago)
+				const CACHE_TTL_MS = 30_000;
+				if (
+					worktree.githubStatus &&
+					Date.now() - worktree.githubStatus.lastRefreshed < CACHE_TTL_MS
+				) {
+					return worktree.githubStatus;
+				}
+
 				// Always fetch fresh data on hover
 				const freshStatus = await fetchGitHubPRStatus(worktree.path);

550-551: Path extraction could yield empty string for trailing-slash paths.

worktree.path.split("/").pop() returns an empty string if the path ends with / (e.g., /path/to/worktree/). Consider using Node's path.basename which handles this correctly.

+import { basename } from "node:path";
 // ...
-				const worktreeName = worktree.path.split("/").pop() ?? worktree.branch;
+				const worktreeName = basename(worktree.path) || worktree.branch;
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (2)

11-11: Consider re-exporting types from a shared location.

The renderer imports the CheckItem type directly from main/lib/db/schemas. While this works for TypeScript types (no runtime Node.js dependency), it creates a coupling between renderer and main process code paths. Consider re-exporting shared types from a shared/ or lib/ location to keep the boundary cleaner.


122-281: Multiple helper components in the same file.

The coding guidelines state "One component per file." However, these are tightly-coupled internal subcomponents (PRStatusBadge, ChecksSummary, ChecksList, CheckItemRow, ReviewStatus) that are only used within WorkspaceHoverCardContent. Keeping them co-located is reasonable for maintainability. Consider extracting to a components/ subdirectory if they grow in complexity or are reused elsewhere.

apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (4)

94-98: Potential shell injection via branch name.

While branch names from git rev-parse are typically safe, maliciously crafted branch names (e.g., containing backticks or $()) could lead to command injection. Consider using a shell-safe approach.

Use an array-based approach or quote the branch name properly:

-		const { stdout } = await execAsync(
-			`gh pr view ${branch} --json number,title,url,state,isDraft,mergedAt,additions,deletions,reviewDecision,statusCheckRollup`,
-			{ cwd: worktreePath },
-		);
+		const { stdout } = await execAsync(
+			`gh pr view "${branch.replace(/"/g, '\\"')}" --json number,title,url,state,isDraft,mergedAt,additions,deletions,reviewDecision,statusCheckRollup`,
+			{ cwd: worktreePath },
+		);

Alternatively, consider using execFile (which doesn't spawn a shell) with argument arrays for better security.


115-125: Fragile error detection via string matching.

Checking for "no pull requests found" in the error message is brittle—GitHub CLI could change wording or localization could affect it.

Consider checking the exit code or using a more robust pattern:

 	} catch (error) {
-		// "no pull requests found" is not an error - just no PR
-		if (
-			error instanceof Error &&
-			error.message.includes("no pull requests found")
-		) {
+		// gh returns exit code 1 with "no pull requests found" when no PR exists
+		if (error instanceof Error && /no pull requests? found/i.test(error.message)) {
 			return null;
 		}
 		// Re-throw other errors to be caught by parent
 		throw error;
 	}

This at least handles singular/plural variations and case differences.


160-174: ACTION_REQUIRED conclusion is not explicitly handled.

The GHCheckContext type includes ACTION_REQUIRED as a possible conclusion (line 18), but it's not mapped in parseChecks. It falls through to "pending", which may be the intended behavior but is implicit.

Consider explicitly handling ACTION_REQUIRED for clarity:

 		} else if (rawStatus === "CANCELLED") {
 			status = "cancelled";
+		} else if (rawStatus === "ACTION_REQUIRED") {
+			status = "pending"; // Requires manual intervention, treat as pending
 		} else {
 			status = "pending";
 		}

48-70: Silent error handling may hinder debugging.

Both fetchGitHubPRStatus and getRepoUrl catch all errors and return null silently. While graceful degradation is good for UX, this could make debugging harder when issues occur.

Consider adding debug-level logging for unexpected errors:

 	} catch (error) {
-		// Any error (gh not installed, not auth'd, etc.) - return null
+		// Any error (gh not installed, not auth'd, etc.) - log and return null
+		if (process.env.NODE_ENV === "development") {
+			console.debug("[github] fetchGitHubPRStatus failed:", error);
+		}
 		return null;
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0240ba and 838e57e.

📒 Files selected for processing (7)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (2 hunks)
  • apps/desktop/src/main/lib/db/schemas.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
apps/desktop/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: Please use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid using any type - use explicit types instead for type safety
Use camelCase for variable and function names following existing codebase patterns
Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Follow existing patterns and match the codebase style when writing new code

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment only

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
**/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/components/**/*.tsx: Create one folder per component with structure: ComponentName/ComponentName.tsx + index.ts for barrel export
Co-locate tests next to the component file they test (e.g., ComponentName.test.tsx)
Co-locate dependencies (utils, hooks, constants, config, stories) next to the file using them
Use nested components/ subdirectory within a parent component only if a sub-component is used 2+ times within that parent; otherwise keep it in the parent's components/ folder
One component per file - avoid multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
apps/desktop/src/renderer/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Call IPC methods using window.ipcRenderer.invoke() with object parameters - TypeScript will infer the exact response type automatically

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
apps/desktop/src/lib/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in shared code like src/lib/electron-router-dom.ts - this code runs in both main and renderer processes

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
apps/desktop/src/main/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Node.js modules (fs, path, os, net, etc.) can be used in main process code only

Files:

  • apps/desktop/src/main/lib/db/schemas.ts
🧬 Code graph analysis (3)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (1)
apps/desktop/src/main/lib/db/schemas.ts (1)
  • CheckItem (17-21)
apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (1)
apps/desktop/src/main/lib/db/schemas.ts (2)
  • GitHubStatus (23-38)
  • CheckItem (17-21)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx (1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (1)
  • WorkspaceHoverCardContent (20-120)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (10)
apps/desktop/src/main/lib/db/schemas.ts (2)

17-21: LGTM!

The CheckItem interface is well-defined with appropriate status union types covering GitHub CI states. The optional url field correctly handles checks that may not have associated links.


30-34: LGTM!

The extended GitHubStatus.pr fields are comprehensive and align well with GitHub's PR data model. The distinction between checksStatus (aggregate) and checks (individual items) provides flexibility for both summary and detailed views.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts (1)

1-1: LGTM!

Clean barrel export following the component folder structure guideline.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx (1)

80-84: LGTM!

Clean prop addition enabling the context menu to access workspace-specific data for the hover card feature.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (2)

20-120: LGTM!

The WorkspaceHoverCardContent component is well-structured with clear conditional rendering for loading, PR present, and no-PR states. Good use of TRPC queries with proper enabled guards.


283-302: LGTM!

The formatRelativeTime helper is simple and effective. It avoids external dependencies for a straightforward use case.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx (2)

8-15: LGTM on imports and module structure.

The imports are properly organized, using the UI library's hover-card primitives and the local WorkspaceHoverCardContent component. This follows the existing codebase patterns.


38-55: Clean composition of HoverCard with ContextMenu.

The nested trigger pattern (HoverCardTrigger wrapping ContextMenuTrigger) with asChild is the correct approach for combining these Radix-based primitives. The delay values (400ms open, 100ms close) provide a good balance between responsiveness and avoiding accidental triggers.

apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (2)

1-5: LGTM on imports.

Using Node.js built-in modules is appropriate here since this file is in the tRPC routers directory which runs in the main process.


180-208: LGTM on computeChecksStatus logic.

The function correctly prioritizes failure over pending, and treats empty/missing rollups as "none". The handling of null/undefined status as pending is appropriate for in-progress checks.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (4)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx (1)

1-18: Tighten config typing to keep status mapping in sync

Implementation is clear. To make this future-proof if you extend the status union, you can tie the config type directly to ReviewStatusProps["status"] so missing mappings become a TS error:

-export function ReviewStatus({ status }: ReviewStatusProps) {
-	const config = {
+export function ReviewStatus({ status }: ReviewStatusProps) {
+	const config: Record<
+		ReviewStatusProps["status"],
+		{ label: string; className: string }
+	> = {
 		approved: { label: "Approved", className: "text-emerald-500" },
@@
 	const { label, className } = config[status];

Purely a type-safety nicety; the current runtime behavior is fine.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx (1)

1-42: Status-to-icon mapping is solid; consider stronger typing for future changes

The visual mapping and link handling look good, and covering all CheckItem statuses makes this easy to scan.

For extra type safety if CheckItem["status"] ever grows, you can tie statusConfig to that union so missing mappings fail at compile time:

-export function CheckItemRow({ check }: CheckItemRowProps) {
-	const statusConfig = {
+export function CheckItemRow({ check }: CheckItemRowProps) {
+	const statusConfig: Record<
+		CheckItem["status"],
+		{ icon: typeof Check; className: string }
+	> = {
 		success: { icon: Check, className: "text-emerald-500" },
		failure: { icon: X, className: "text-destructive-foreground" },
		pending: { icon: LoaderCircle, className: "text-amber-500" },
		skipped: { icon: Minus, className: "text-muted-foreground" },
		cancelled: { icon: Minus, className: "text-muted-foreground" },
	};

Optional, but helps keep things in sync if backend statuses evolve.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx (1)

1-27: PRStatusBadge mapping is clear; small type-safety improvement possible

The badge styling/labels are straightforward and match the state union. Similar to ReviewStatus, you can bind the maps to the prop union so adding a new state forces you to update both:

 export function PRStatusBadge({ state }: PRStatusBadgeProps) {
-	const styles = {
+	const styles: Record<PRStatusBadgeProps["state"], string> = {
 		open: "bg-emerald-500/15 text-emerald-500",
 		draft: "bg-muted text-muted-foreground",
 		merged: "bg-violet-500/15 text-violet-500",
 		closed: "bg-destructive/15 text-destructive-foreground",
 	};
 
-	const labels = {
+	const labels: Record<PRStatusBadgeProps["state"], string> = {
 		open: "Open",
 		draft: "Draft",
 		merged: "Merged",
 		closed: "Closed",
 	};

Not required, but makes the component safer to extend later.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (1)

18-27: Consider handling query error states.

Both tRPC queries handle loading state but silently fail on errors. If getGitHubStatus errors (e.g., gh CLI unavailable), the component falls through to the "No PR" state, which may be acceptable as a graceful fallback. However, getWorktreeInfo errors would leave the header section empty without feedback.

If this graceful degradation is intentional, consider adding a brief inline comment for future maintainers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 838e57e and e47e42c.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • AGENTS.md (2 hunks)
  • apps/desktop/package.json (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/index.ts
🧰 Additional context used
📓 Path-based instructions (7)
apps/desktop/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: Please use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid using any type - use explicit types instead for type safety
Use camelCase for variable and function names following existing codebase patterns
Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Follow existing patterns and match the codebase style when writing new code

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
**/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/components/**/*.tsx: Create one folder per component with structure: ComponentName/ComponentName.tsx + index.ts for barrel export
Co-locate tests next to the component file they test (e.g., ComponentName.test.tsx)
Co-locate dependencies (utils, hooks, constants, config, stories) next to the file using them
Use nested components/ subdirectory within a parent component only if a sub-component is used 2+ times within that parent; otherwise keep it in the parent's components/ folder
One component per file - avoid multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment only

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
apps/desktop/src/renderer/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Call IPC methods using window.ipcRenderer.invoke() with object parameters - TypeScript will infer the exact response type automatically

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
AGENTS.md

📄 CodeRabbit inference engine (CLAUDE.md)

Document agent responsibilities and interactions in AGENTS.md

Files:

  • AGENTS.md
🧠 Learnings (13)
📚 Learning: 2025-11-24T21:32:21.725Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/CLAUDE.md:0-0
Timestamp: 2025-11-24T21:32:21.725Z
Learning: Applies to apps/desktop/**/AGENTS.md : Document agent responsibilities, capabilities, and interaction patterns in AGENTS.md

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T21:32:17.800Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:32:17.800Z
Learning: Applies to AGENTS.md : Document agent responsibilities and interactions in AGENTS.md

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : Co-locate dependencies (utils, hooks, constants, config, stories) next to the file using them

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : Create one folder per component with structure: `ComponentName/ComponentName.tsx` + `index.ts` for barrel export

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : Co-locate tests next to the component file they test (e.g., `ComponentName.test.tsx`)

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : Use nested `components/` subdirectory within a parent component only if a sub-component is used 2+ times within that parent; otherwise keep it in the parent's `components/` folder

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : One component per file - avoid multi-component files

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to packages/ui/src/**/*.tsx : Use shadcn/ui components and TailwindCSS v4 for all UI component styling in the shared UI package

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to packages/db/src/**/*.ts : Define all database schema in `packages/db/src/` using Drizzle ORM

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/*.{ts,tsx} : Follow existing patterns and match the codebase style when writing new code

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/*.{ts,tsx} : Use camelCase for variable and function names following existing codebase patterns

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.test.{ts,tsx,js,jsx} : Tests should be repeatable

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
🧬 Code graph analysis (5)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx (1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/index.ts (1)
  • ReviewStatus (1-1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx (2)
apps/desktop/src/main/lib/db/schemas.ts (1)
  • CheckItem (17-21)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/index.ts (1)
  • ChecksSummary (1-1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx (2)
apps/desktop/src/main/lib/db/schemas.ts (1)
  • CheckItem (17-21)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts (1)
  • CheckItemRow (1-1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx (1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts (1)
  • PRStatusBadge (1-1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx (1)
apps/desktop/src/main/lib/db/schemas.ts (1)
  • CheckItem (17-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (9)
apps/desktop/package.json (1)

55-55: New date utility dependency looks appropriate for hover-card time formatting

Adding a date utility here makes sense for the worktree age/relative time display in the hover card; no issues from a package-layout perspective. Please just confirm that your Electron/Vite setup is happy with the current major version of this library (ESM expectations, tree-shaking, etc.).

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts (1)

1-1: Barrel export is consistent with component structure

Re-exporting ChecksList from an index.ts matches the component-folder guideline and keeps imports clean.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts (1)

1-1: PRStatusBadge barrel export matches existing patterns

This keeps consumer imports tidy and matches how other hover-card subcomponents are exposed.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (1)

32-113: LGTM!

The component structure is well-organized with clear state handling for loading, PR present, and no-PR scenarios. The conditional rendering logic is clean and the sub-components are properly composed.

AGENTS.md (2)

80-103: LGTM!

The expanded project structure documentation provides clear guidance on organizing hooks, utils, stores, and providers alongside components. This aligns well with the co-location principles in the coding guidelines.


141-143: LGTM!

Good addition documenting the shadcn/ui exception for kebab-case files. This prevents confusion when developers encounter the different naming convention in UI component directories.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx (2)

9-42: LGTM!

The component logic is clean with proper handling of all status states. The passing/total calculation correctly excludes skipped and cancelled checks, and the config-driven icon mapping is a nice pattern.


2-2: The import of CheckItem from main/lib/db/schemas in renderer code is acceptable—it's a type-only import (erased at runtime) and uses the alias correctly per AGENTS.md guidelines. While moving this type to src/shared/types.ts would be a reasonable refactoring since shared types are co-located there, AGENTS.md contains no architectural rule requiring this change. The current pattern is consistent with how other database schemas are structured.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts (1)

1-1: LGTM! Standard barrel export pattern.

The re-export follows common TypeScript/React conventions, allowing cleaner imports of the CheckItemRow component.

Comment on lines +6 to +43
interface ChecksListProps {
checks: CheckItem[];
}

export function ChecksList({ checks }: ChecksListProps) {
const [expanded, setExpanded] = useState(false);

// Filter out skipped/cancelled for display count, but show all when expanded
const relevantChecks = checks.filter(
(c) => c.status !== "skipped" && c.status !== "cancelled",
);

if (relevantChecks.length === 0) return null;

return (
<div className="text-xs">
<button
type="button"
onClick={() => setExpanded(!expanded)}
className="flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors"
>
{expanded ? (
<ChevronDown className="size-3" />
) : (
<ChevronRight className="size-3" />
)}
<span>{expanded ? "Hide checks" : "Show checks"}</span>
</button>

{expanded && (
<div className="mt-1.5 space-y-1 pl-1">
{relevantChecks.map((check) => (
<CheckItemRow key={check.name} check={check} />
))}
</div>
)}
</div>
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Checks filtering contradicts the comment and likely intended UX

Right now:

  • You filter out skipped/cancelled into relevantChecks.
  • You return null when relevantChecks.length === 0.
  • You also render the expanded list from relevantChecks.

So skipped/cancelled checks are never shown, even when expanded, and a list containing only those statuses disappears entirely. That conflicts with the comment “show all when expanded” and can hide real CI jobs.

If you want the expanded list to show every check while still bailing out only when there are literally no checks, a minimal adjustment would be:

-	// Filter out skipped/cancelled for display count, but show all when expanded
-	const relevantChecks = checks.filter(
-		(c) => c.status !== "skipped" && c.status !== "cancelled",
-	);
-
-	if (relevantChecks.length === 0) return null;
+	if (checks.length === 0) return null;
@@
-			{expanded && (
-				<div className="mt-1.5 space-y-1 pl-1">
-					{relevantChecks.map((check) => (
-						<CheckItemRow key={check.name} check={check} />
-					))}
-				</div>
-			)}
+			{expanded && (
+				<div className="mt-1.5 space-y-1 pl-1">
+					{checks.map((check) => (
+						<CheckItemRow key={check.name} check={check} />
+					))}
+				</div>
+			)}

If the intent is instead to always hide skipped/cancelled, I’d at least update the comment to avoid misleading future readers.

Also, if there’s any chance of duplicate check.name values within a PR, consider using a more stable key (e.g. a backend-provided id) when/if it becomes available.

saddlepaddle and others added 3 commits December 6, 2025 08:59
- Add hover card to workspace tabs showing worktree info and GitHub PR status
- Fetch PR data via `gh` CLI with graceful fallback if unavailable
- Display worktree name, age, rebase warning, PR number/status/title
- Show diff stats (+additions/-deletions) with GitHub-style colors
- Add expandable checks list showing individual CI job statuses with links
- Show review status (approved/changes requested/pending)
- Include "View on GitHub" button with GitHub logo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Break down the monolithic WorkspaceHoverCard.tsx into separate components
following the repo's co-location pattern:

- PRStatusBadge: Badge showing PR state (open/draft/merged/closed)
- ReviewStatus: Shows review decision status
- ChecksSummary: Summary of CI checks (X/Y passing)
- ChecksList: Expandable list of individual checks with CheckItemRow
- utils.ts: Shared formatRelativeTime utility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update AGENTS.md with expanded project structure guidelines
- Add shadcn/ui exception section for kebab-case files
- Move CheckItemRow to proper nested folder structure
- Replace custom formatRelativeTime with date-fns formatDistanceToNow
- Delete unnecessary utils.ts wrapper file

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@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: 0

♻️ Duplicate comments (1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx (1)

13-41: Filtering behavior contradicts the comment.

The comment states "show all when expanded" but the expanded list only renders relevantChecks (excluding skipped/cancelled). This was already flagged in a previous review. Either update the comment to match the behavior, or change the expanded list to render all checks as the comment suggests.

🧹 Nitpick comments (3)
apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (2)

95-98: Consider shell-escaping the branch name to prevent command injection.

While git branch names have restrictions that limit most attack vectors, directly interpolating ${branch} into a shell command could be problematic if a branch name contains shell metacharacters. Consider using an array-based exec or explicit escaping.

-		const { stdout } = await execAsync(
-			`gh pr view ${branch} --json number,title,url,state,isDraft,mergedAt,additions,deletions,reviewDecision,statusCheckRollup`,
-			{ cwd: worktreePath },
-		);
+		const { stdout } = await execAsync(
+			`gh pr view "${branch}" --json number,title,url,state,isDraft,mergedAt,additions,deletions,reviewDecision,statusCheckRollup`,
+			{ cwd: worktreePath },
+		);

Alternatively, use execFile with an argument array for safer subprocess invocation:

import { execFile } from "node:child_process";
const execFileAsync = promisify(execFile);

const { stdout } = await execFileAsync(
  "gh",
  ["pr", "view", branch, "--json", "number,title,url,state,isDraft,mergedAt,additions,deletions,reviewDecision,statusCheckRollup"],
  { cwd: worktreePath },
);

159-174: ACTION_REQUIRED conclusion falls through to pending.

GitHub's ACTION_REQUIRED conclusion typically indicates the check needs manual intervention. The current logic treats it as pending, which may be reasonable, but consider whether mapping it to failure would better communicate that user action is needed.

 		} else if (rawStatus === "CANCELLED") {
 			status = "cancelled";
+		} else if (rawStatus === "ACTION_REQUIRED") {
+			status = "failure"; // Requires manual intervention
 		} else {
 			status = "pending";
 		}
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx (1)

12-15: Optional: compute passing/total in a single pass and consider label tweak

You can slightly simplify/optimize by deriving passing and total in one pass, and optionally make the label a bit clearer:

- const passing = checks.filter((c) => c.status === "success").length;
- const total = checks.filter(
-   (c) => c.status !== "skipped" && c.status !== "cancelled",
- ).length;
+ const { passing, total } = checks.reduce(
+   (acc, c) => {
+     if (c.status !== "skipped" && c.status !== "cancelled") {
+       acc.total += 1;
+       if (c.status === "success") acc.passing += 1;
+     }
+     return acc;
+   },
+   { passing: 0, total: 0 },
+ );

If you want a bit more explicit copy, you could also switch the label to something like "Checks""No checks" when total === 0, or "1/3 checks passing" when total > 0, but that’s purely stylistic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e47e42c and 3c5b3a0.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • AGENTS.md (2 hunks)
  • apps/desktop/package.json (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (2 hunks)
  • apps/desktop/src/main/lib/db/schemas.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • apps/desktop/package.json
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
🧰 Additional context used
📓 Path-based instructions (9)
apps/desktop/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc

Files:

  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: Please use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Files:

  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid using any type - use explicit types instead for type safety
Use camelCase for variable and function names following existing codebase patterns
Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Follow existing patterns and match the codebase style when writing new code

Files:

  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
apps/desktop/src/main/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Node.js modules (fs, path, os, net, etc.) can be used in main process code only

Files:

  • apps/desktop/src/main/lib/db/schemas.ts
apps/desktop/src/lib/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in shared code like src/lib/electron-router-dom.ts - this code runs in both main and renderer processes

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
**/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/components/**/*.tsx: Create one folder per component with structure: ComponentName/ComponentName.tsx + index.ts for barrel export
Co-locate tests next to the component file they test (e.g., ComponentName.test.tsx)
Co-locate dependencies (utils, hooks, constants, config, stories) next to the file using them
Use nested components/ subdirectory within a parent component only if a sub-component is used 2+ times within that parent; otherwise keep it in the parent's components/ folder
One component per file - avoid multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment only

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
apps/desktop/src/renderer/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Call IPC methods using window.ipcRenderer.invoke() with object parameters - TypeScript will infer the exact response type automatically

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
AGENTS.md

📄 CodeRabbit inference engine (CLAUDE.md)

Document agent responsibilities and interactions in AGENTS.md

Files:

  • AGENTS.md
🧠 Learnings (12)
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.test.{ts,tsx,js,jsx} : Tests should be repeatable

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
📚 Learning: 2025-11-24T21:32:21.725Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/CLAUDE.md:0-0
Timestamp: 2025-11-24T21:32:21.725Z
Learning: Applies to apps/desktop/**/AGENTS.md : Document agent responsibilities, capabilities, and interaction patterns in AGENTS.md

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : Co-locate dependencies (utils, hooks, constants, config, stories) next to the file using them

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : Create one folder per component with structure: `ComponentName/ComponentName.tsx` + `index.ts` for barrel export

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : Co-locate tests next to the component file they test (e.g., `ComponentName.test.tsx`)

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : Use nested `components/` subdirectory within a parent component only if a sub-component is used 2+ times within that parent; otherwise keep it in the parent's `components/` folder

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/components/**/*.tsx : One component per file - avoid multi-component files

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to packages/ui/src/**/*.tsx : Use shadcn/ui components and TailwindCSS v4 for all UI component styling in the shared UI package

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to packages/db/src/**/*.ts : Define all database schema in `packages/db/src/` using Drizzle ORM

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/*.{ts,tsx} : Follow existing patterns and match the codebase style when writing new code

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/*.{ts,tsx} : Use camelCase for variable and function names following existing codebase patterns

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Applied to files:

  • AGENTS.md
🧬 Code graph analysis (5)
apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (1)
apps/desktop/src/main/lib/db/schemas.ts (2)
  • GitHubStatus (25-40)
  • CheckItem (19-23)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (4)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx (1)
  • PRStatusBadge (5-27)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx (1)
  • ChecksSummary (9-43)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx (1)
  • ReviewStatus (5-18)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx (1)
  • ChecksList (10-44)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx (2)
packages/ui/src/components/hover-card.tsx (3)
  • HoverCard (42-42)
  • HoverCardTrigger (42-42)
  • HoverCardContent (42-42)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (1)
  • WorkspaceHoverCardContent (15-115)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx (2)
apps/desktop/src/main/lib/db/schemas.ts (1)
  • CheckItem (19-23)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx (1)
  • CheckItemRow (8-42)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx (2)
apps/desktop/src/main/lib/db/schemas.ts (1)
  • CheckItem (19-23)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/index.ts (1)
  • ChecksSummary (1-1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (8)
apps/desktop/src/main/lib/db/schemas.ts (1)

19-36: Well-structured type definitions for GitHub PR status.

The CheckItem interface and GitHubStatus.pr extensions are properly typed with appropriate union types that cover the expected GitHub states. The separation between checksStatus (aggregate) and checks (individual items) provides good flexibility for the UI layer.

AGENTS.md (2)

80-103: Good documentation of modular project structure.

The expanded structure example clearly illustrates the co-location patterns for hooks, utils, stores, and providers. This aligns well with the coding guidelines.


141-143: Helpful exception documentation for shadcn/ui components.

Documenting that shadcn/ui components use kebab-case single files (instead of the standard folder structure) prevents future confusion and explains the intentional divergence from the normal pattern.

apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (1)

48-70: Clean error handling with graceful fallback.

The function properly swallows errors when gh is unavailable or not authenticated, returning null to allow the UI to gracefully degrade. The separation of concerns between getRepoUrl and getPRForCurrentBranch is well-structured.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx (2)

18-27: Well-structured data fetching with conditional enabling.

The TRPC queries properly use enabled: !!workspaceId to prevent fetching when no workspace is selected. The separation between worktree info and GitHub status allows for independent loading states.


57-112: Comprehensive state handling in PR section.

The component properly handles all UI states: loading (isLoadingGithub), PR exists (pr), no PR but GitHub status available (githubStatus without pr), and no GitHub status at all. The checks and review status are appropriately shown only for open PRs.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx (1)

39-55: Clean integration of HoverCard with existing ContextMenu.

The composition properly nests HoverCard around ContextMenu, using asChild to avoid extra DOM wrappers. The delay values (400ms open, 100ms close) provide good UX by preventing accidental triggers while still feeling responsive.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx (1)

1-43: ChecksSummary component looks correct and aligns with guidelines

  • Props are well-typed (CheckItem[] and narrow status union), no any.
  • status === "none" guard cleanly prevents invalid config indexing and avoids rendering noise.
  • Count logic (passing vs total excluding skipped/cancelled) is reasonable, and icon/color mappings match the status semantics.
  • Renderer file correctly uses a TS alias import and does not pull in Node APIs.

No blocking issues from this file.

- Validate gh CLI output with Zod schemas instead of type assertions
- Derive TypeScript types from Zod schemas using z.infer
- Add 30-second TTL cache to reduce GitHub API calls

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (1)

9-28: Consider using z.string() instead of strict enums for future-proofing.

GitHub may introduce new check states or conclusions over time (e.g., EXPECTED, STALE, STARTUP_FAILURE). If the gh CLI returns an unexpected value, z.parse() will throw and the entire PR fetch fails. While the parent catch returns null, this could unexpectedly hide valid PR data.

You could use z.string() and handle unknown values in parseChecks/computeChecksStatus, or use .catch() to provide defaults.

 const GHCheckContextSchema = z.object({
 	__typename: z.string(),
 	name: z.string().optional(),
 	context: z.string().optional(),
-	state: z.enum(["SUCCESS", "FAILURE", "PENDING", "ERROR"]).optional(),
+	state: z.string().optional(),
 	conclusion: z
-		.enum([
-			"SUCCESS",
-			"FAILURE",
-			"CANCELLED",
-			"SKIPPED",
-			"TIMED_OUT",
-			"ACTION_REQUIRED",
-			"NEUTRAL",
-		])
+		.string()
 		.nullable()
 		.optional(),
 	detailsUrl: z.string().optional(),
 	targetUrl: z.string().optional(),
 });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c5b3a0 and bf6ab6a.

📒 Files selected for processing (1)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/desktop/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: Please use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid using any type - use explicit types instead for type safety
Use camelCase for variable and function names following existing codebase patterns
Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Follow existing patterns and match the codebase style when writing new code

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
apps/desktop/src/lib/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in shared code like src/lib/electron-router-dom.ts - this code runs in both main and renderer processes

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts
🧬 Code graph analysis (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (1)
apps/desktop/src/main/lib/db/schemas.ts (2)
  • GitHubStatus (25-40)
  • CheckItem (19-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (7)
apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts (7)

1-7: LGTM!

Imports are appropriate for main-process code, and the alias usage follows tsconfig patterns.


55-57: LGTM!

Simple and effective caching strategy. The 30-second TTL is reasonable for hover card data. For a desktop app with a limited number of worktrees, the unbounded cache size is acceptable.


64-97: LGTM!

The function correctly implements caching with TTL check, handles the fetch flow appropriately, and the error handling aligns with the documented graceful fallback behavior.


99-109: LGTM!

The command string is static with no user input interpolation, and the error handling provides graceful fallback.


155-171: LGTM!

Both mapping functions correctly handle all expected states with clear logic.


173-205: LGTM!

Correctly handles both CheckRun and StatusContext types from GitHub's API with appropriate status mapping. The fallback to "pending" for unrecognized states is a reasonable default.


207-235: LGTM!

The aggregation logic correctly prioritizes failure over pending, and treats skipped/cancelled checks as non-blocking, which aligns with GitHub's semantics.

Comment on lines +122 to +125
const { stdout } = await execAsync(
`gh pr view ${branch} --json number,title,url,state,isDraft,mergedAt,additions,deletions,reviewDecision,statusCheckRollup`,
{ cwd: worktreePath },
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Command injection risk: escape or avoid shell interpolation of branch name.

The branch variable is directly interpolated into a shell command. Git branch names can contain shell metacharacters ($, backticks) that would be interpreted by the shell. A malicious branch name like test$(malicious_command) could execute arbitrary code.

Use execFile to avoid shell interpretation, or properly quote the argument:

+import { execFile } from "node:child_process";
+const execFileAsync = promisify(execFile);

 async function getPRForCurrentBranch(
 	worktreePath: string,
 ): Promise<GitHubStatus["pr"]> {
 	try {
 		const { stdout: branchName } = await execAsync(
 			"git rev-parse --abbrev-ref HEAD",
 			{ cwd: worktreePath },
 		);
 		const branch = branchName.trim();

-		const { stdout } = await execAsync(
-			`gh pr view ${branch} --json number,title,url,state,isDraft,mergedAt,additions,deletions,reviewDecision,statusCheckRollup`,
-			{ cwd: worktreePath },
-		);
+		const { stdout } = await execFileAsync(
+			"gh",
+			[
+				"pr",
+				"view",
+				branch,
+				"--json",
+				"number,title,url,state,isDraft,mergedAt,additions,deletions,reviewDecision,statusCheckRollup",
+			],
+			{ cwd: worktreePath },
+		);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/desktop/src/lib/trpc/routers/workspaces/utils/github.ts around lines
122-125 the code interpolates the branch name directly into a shell command
which allows command injection; change to invoke the GitHub CLI without shell
interpolation by using an execFile-style call (or the execAsync equivalent that
accepts an args array) and pass "pr", "view", branch, and the --json string as
separate arguments, keeping the cwd option; this ensures the branch name is not
interpreted by a shell and prevents execution of embedded metacharacters.

- reviewDecision can be empty string (not just null)
- statusCheckRollup is a flat array, not { contexts: [...] }
- Add missing fields: status, startedAt, completedAt, workflowName
- conclusion can be empty string when check is in progress
- Treat empty string as pending in checksStatus computation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Security fix:
- Replace exec() with execFile() to prevent shell injection via branch names
- Pass gh CLI arguments as array instead of interpolated string

Structure:
- Move github.ts to github/github.ts with index.ts barrel export
- Extract Zod schemas to co-located types.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.

1 participant