Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/agent-manager-pr-status-icon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Color Agent Manager PR badges by pull request state (open, draft, merged, closed) and show CI and review status as a separate icon, so a failing check is no longer mistaken for a closed PR.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 48 additions & 6 deletions packages/kilo-vscode/webview-ui/agent-manager/WorktreeItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Sidebar worktree item with inline delete confirmation, HoverCard, rename, and stats.
* Extracted from AgentManagerApp for reuse and visual-regression testing via Storybook.
*/
import { Component, For, Show, createSignal } from "solid-js"
import { Component, For, Match, Show, Switch, createSignal } from "solid-js"
import { Icon } from "@kilocode/kilo-ui/icon"
import { IconButton } from "@kilocode/kilo-ui/icon-button"
import { Spinner } from "@kilocode/kilo-ui/spinner"
Expand Down Expand Up @@ -84,17 +84,43 @@ const MAX_SHORTCUT = 9
const hasStats = (s: WorktreeGitStats | undefined): s is WorktreeGitStats =>
!!s && (s.files > 0 || s.additions > 0 || s.deletions > 0 || s.ahead > 0 || s.behind > 0)

/** Returns the accent color for a PR badge based on state priority. */
/**
* Accent color for a PR badge, derived from the PR's lifecycle state
* (open/draft/merged/closed). The one exception is an open PR with checks still
* running, which uses amber and pulses its background (see prChecksRunning) — a
* transient, unambiguous signal since no lifecycle state uses amber. Terminal CI
* and review results are conveyed by a separate status icon (see prBadgeIndicator)
* so a failing check is not mistaken for a closed PR.
*/
export function prAccentColor(pr: PRStatus): string {
if (pr.state === "draft") return "var(--text-weaker)"
if (pr.state === "merged") return "#a78bfa"
if (pr.state === "closed") return "#f87171"
if (pr.checks.status === "failure") return "#ef4444"
if (pr.review === "changes_requested") return "#fbbf24"
if (pr.checks.status === "pending") return "#fbbf24"
return "#34d399"
}

/** True while an open PR's checks are still running — drives the pulsing amber badge. */
export function prChecksRunning(pr: PRStatus): boolean {
return pr.state === "open" && pr.checks.status === "pending"
}

export type PRBadgeIndicator = "failure" | "changes" | "approved" | "none"

/**
* Terminal CI/review status shown as an icon overlaid on the PR badge, independent
* of the badge's state-based accent color. Running checks are not represented here —
* they are shown by the pulsing amber background instead. Terminal PRs (merged/closed)
* show no indicator since their checks are no longer actionable.
*/
export function prBadgeIndicator(pr: PRStatus): PRBadgeIndicator {
if (pr.state === "merged" || pr.state === "closed") return "none"
if (pr.checks.status === "failure") return "failure"
if (pr.review === "changes_requested") return "changes"
if (pr.review === "approved") return "approved"
return "none"
}

function prStateLabel(state: PRStatus["state"]): string {
if (state === "draft") return "Draft"
if (state === "merged") return "Merged"
Expand Down Expand Up @@ -315,14 +341,30 @@ export const WorktreeItem: Component<WorktreeItemProps> = (props) => {
>
{(pr) => {
const accent = () => prAccentColor(pr())
const indicator = () => prBadgeIndicator(pr())
return (
<span
class="am-pr-badge"
style={{ "--pr-accent": accent() }}
data-pending={pr().state === "open" && pr().checks.status === "pending" ? "" : undefined}
data-pending={prChecksRunning(pr()) ? "" : undefined}
onClick={handleOpenPR}
>
<Icon name={pr().review === "approved" ? "check-small" : "branch"} size="small" />
<Switch fallback={<Icon name="branch" size="small" />}>

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.

WARNING: Terminal PRs still render a status icon

prBadgeIndicator() returns "none" for merged and closed PRs, but this Switch falls back to the neutral branch icon. That means terminal badges still render a leading glyph even though the new contract says merged/closed PRs should show no status icon. Consider handling "none" explicitly and rendering nothing for that case.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

<Match when={indicator() === "failure"}>
<Icon name="circle-x" size="small" class="am-pr-badge-status" data-status="failure" />
</Match>
<Match when={indicator() === "changes"}>
<Icon name="warning" size="small" class="am-pr-badge-status" data-status="changes" />
</Match>
<Match when={indicator() === "approved"}>
<Icon
name="circle-check"
size="small"
class="am-pr-badge-status"
data-status="approved"
/>
</Match>
</Switch>
<span class="am-pr-badge-number">#{pr().number}</span>
</span>
)
Expand Down
13 changes: 13 additions & 0 deletions packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,19 @@ button.am-section-toggle:hover .am-section-label {
.am-pr-badge:hover .am-pr-badge-number {
color: var(--pr-accent);
}
/* Status icon overlaid on the badge — colored by CI/review result, independent
of the badge's PR-state accent color. */
.am-pr-badge-status[data-status="failure"] {
color: #ef4444;
}
.am-pr-badge-status[data-status="changes"] {
color: #fbbf24;
}
.am-pr-badge-status[data-status="approved"] {
color: #34d399;
}

/* Checks still running — pulse the whole amber badge. */
.am-pr-badge[data-pending] {
animation: am-pr-pulse 1.5s ease-in-out infinite;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,10 +573,11 @@ export const WithPRBadges: Story = {
name: "Section — worktrees with PR badges",
render: () => (
<StoryProviders noPadding>
<div style={{ "max-height": "400px", overflow: "auto" }}>
<div style={{ "max-height": "560px", overflow: "auto" }}>
<DndWrap>
<SectionHeader section={sec("s1", 0, { name: "In Review", color: "Blue" })} count={3} {...sectionProps}>
<SectionHeader section={sec("s1", 0, { name: "In Review", color: "Blue" })} count={8} {...sectionProps}>

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.

SUGGESTION: Story count no longer matches the rendered badge states

This header count was changed to 8, but the story below renders seven WorktreeItems and the PR description also describes seven states. Leaving it at 8 makes the visual-regression fixture and generated screenshot show a mismatched section count.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

<div class="am-section-group-body">
{/* Open + passing + approved → green badge, green check */}
<WorktreeItem
{...wtProps}
worktree={wt("wt-1", "feat/api-v2")}
Expand All @@ -595,6 +596,7 @@ export const WithPRBadges: Story = {
checks: { status: "success", total: 5, passed: 5, failed: 0, pending: 0, items: [] },
}}
/>
{/* Open + failing checks → green badge, red ✗ (no longer confusable with closed) */}
<WorktreeItem
{...wtProps}
worktree={wt("wt-2", "fix/race-cond")}
Expand All @@ -605,28 +607,101 @@ export const WithPRBadges: Story = {
title: "fix: race condition",
url: "#",
state: "open",
review: "changes_requested",
review: null,
additions: 15,
deletions: 8,
files: 3,
checks: { status: "failure", total: 5, passed: 3, failed: 2, pending: 0, items: [] },
}}
/>
{/* Open + changes requested → green badge, amber warning */}
<WorktreeItem
{...wtProps}
worktree={wt("wt-3", "feat/cache")}
worktree={wt("wt-3", "feat/search")}
label="feat/search"
subtitle="feat/search"
pr={{
number: 91,
title: "feat: search",
url: "#",
state: "open",
review: "changes_requested",
additions: 60,
deletions: 12,
files: 4,
checks: { status: "success", total: 5, passed: 5, failed: 0, pending: 0, items: [] },
}}
/>
{/* Open + checks running → pulsing amber badge (animation disabled in snapshots) */}
<WorktreeItem
{...wtProps}
worktree={wt("wt-4", "feat/cache")}
label="feat/cache"
subtitle="feat/cache"
pr={{
number: 103,
title: "feat: cache layer",
url: "#",
state: "draft",
state: "open",
review: null,
additions: 200,
deletions: 0,
files: 8,
checks: { status: "pending", total: 5, passed: 0, failed: 0, pending: 5, items: [] },
checks: { status: "pending", total: 5, passed: 2, failed: 0, pending: 3, items: [] },
}}
/>
{/* Draft → gray badge */}
<WorktreeItem
{...wtProps}
worktree={wt("wt-5", "wip/refactor")}
label="wip/refactor"
subtitle="wip/refactor"
pr={{
number: 110,
title: "wip: refactor",
url: "#",
state: "draft",
review: null,
additions: 30,
deletions: 5,
files: 2,
checks: { status: "none", total: 0, passed: 0, failed: 0, pending: 0, items: [] },
}}
/>
{/* Merged → purple badge, no status icon */}
<WorktreeItem
{...wtProps}
worktree={wt("wt-6", "feat/done")}
label="feat/done"
subtitle="feat/done"
pr={{
number: 70,
title: "feat: done",
url: "#",
state: "merged",
review: "approved",
additions: 90,
deletions: 20,
files: 6,
checks: { status: "success", total: 5, passed: 5, failed: 0, pending: 0, items: [] },
}}
/>
{/* Closed → red badge, no status icon (distinct from a failing open PR) */}
<WorktreeItem
{...wtProps}
worktree={wt("wt-7", "spike/idea")}
label="spike/idea"
subtitle="spike/idea"
pr={{
number: 65,
title: "spike: idea",
url: "#",
state: "closed",
review: null,
additions: 10,
deletions: 4,
files: 1,
checks: { status: "failure", total: 5, passed: 1, failed: 4, pending: 0, items: [] },
}}
/>
</div>
Expand Down
Loading