diff --git a/src/renderer/src/components/WorktreeJumpPalette.tsx b/src/renderer/src/components/WorktreeJumpPalette.tsx
index defa9db4a..cfa7f2a6d 100644
--- a/src/renderer/src/components/WorktreeJumpPalette.tsx
+++ b/src/renderer/src/components/WorktreeJumpPalette.tsx
@@ -158,6 +158,7 @@ export default function WorktreeJumpPalette(): React.JSX.Element | null {
const ptyIdsByTabId = useAppStore((s) => s.ptyIdsByTabId)
const prCache = useAppStore((s) => s.prCache)
const issueCache = useAppStore((s) => s.issueCache)
+ const agentStatusByPaneKey = useAppStore((s) => s.agentStatusByPaneKey)
const activeWorktreeId = useAppStore((s) => s.activeWorktreeId)
const activeTabType = useAppStore((s) => s.activeTabType)
const activeBrowserTabId = useAppStore((s) => s.activeBrowserTabId)
@@ -233,8 +234,8 @@ export default function WorktreeJumpPalette(): React.JSX.Element | null {
visibleWorktrees,
tabsByWorktree,
repoMap,
- prCache,
- undefined,
+ agentStatusByPaneKey,
+ runtimePaneTitlesByTabId,
ptyIdsByTabId
)
: switchableWorktreesForRows,
@@ -244,7 +245,8 @@ export default function WorktreeJumpPalette(): React.JSX.Element | null {
switchableWorktreesForRows,
tabsByWorktree,
repoMap,
- prCache,
+ agentStatusByPaneKey,
+ runtimePaneTitlesByTabId,
ptyIdsByTabId
]
)
@@ -260,11 +262,18 @@ export default function WorktreeJumpPalette(): React.JSX.Element | null {
allWorktrees,
tabsByWorktree,
repoMap,
- prCache,
- undefined,
+ agentStatusByPaneKey,
+ runtimePaneTitlesByTabId,
ptyIdsByTabId
)
- }, [allWorktrees, tabsByWorktree, repoMap, prCache, ptyIdsByTabId])
+ }, [
+ allWorktrees,
+ tabsByWorktree,
+ repoMap,
+ agentStatusByPaneKey,
+ runtimePaneTitlesByTabId,
+ ptyIdsByTabId
+ ])
// Why: browser rows need worktree lookups for repo badge colors, and browser
// search intentionally includes archived worktrees. This map must cover all
diff --git a/src/renderer/src/components/sidebar/SidebarHeader.tsx b/src/renderer/src/components/sidebar/SidebarHeader.tsx
index 5757f44a6..e84bde04b 100644
--- a/src/renderer/src/components/sidebar/SidebarHeader.tsx
+++ b/src/renderer/src/components/sidebar/SidebarHeader.tsx
@@ -38,10 +38,14 @@ const PROPERTY_OPTIONS: { id: WorktreeCardProperty; label: string }[] = [
]
const SORT_OPTIONS = [
- { id: 'name', label: 'Name' },
- { id: 'smart', label: 'Smart' },
- { id: 'recent', label: 'Recent' },
- { id: 'repo', label: 'Repo' }
+ { id: 'name', label: 'Name', description: null },
+ {
+ id: 'smart',
+ label: 'Smart',
+ description: 'Agents that need attention, then most recent activity.'
+ },
+ { id: 'recent', label: 'Recent', description: null },
+ { id: 'repo', label: 'Repo', description: null }
] as const
const isMac = navigator.userAgent.includes('Mac')
@@ -116,17 +120,30 @@ const SidebarHeader = React.memo(function SidebarHeader() {
value={sortBy}
onValueChange={(v) => setSortBy(v as typeof sortBy)}
>
- {SORT_OPTIONS.map((opt) => (
- e.preventDefault()}
- >
- {opt.label}
-
- ))}
+ {SORT_OPTIONS.map((opt) => {
+ const radioItem = (
+ e.preventDefault()}
+ >
+ {opt.label}
+
+ )
+ if (!opt.description) {
+ return radioItem
+ }
+ return (
+
+ {radioItem}
+
+ {opt.description}
+
+
+ )
+ })}
diff --git a/src/renderer/src/components/sidebar/WorktreeList.tsx b/src/renderer/src/components/sidebar/WorktreeList.tsx
index 356f33583..ef2250db6 100644
--- a/src/renderer/src/components/sidebar/WorktreeList.tsx
+++ b/src/renderer/src/components/sidebar/WorktreeList.tsx
@@ -15,12 +15,14 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip
import { cn } from '@/lib/utils'
import type { Worktree, Repo } from '../../../../shared/types'
import { isGitRepoKind } from '../../../../shared/repo-kind'
+import { buildWorktreeComparator } from './smart-sort'
import {
- buildExplicitEntriesByTabId,
- buildWorktreeComparator,
- computeSmartScore,
- hasAnyLivePty
-} from './smart-sort'
+ buildAttentionByWorktree,
+ type SmartClass,
+ type WorktreeAttention
+} from './smart-attention'
+import { track } from '@/lib/telemetry'
+import { tabHasLivePty } from '@/lib/tab-has-live-pty'
import {
type GroupHeaderRow,
type Row,
@@ -585,10 +587,10 @@ const WorktreeList = React.memo(function WorktreeList() {
const cardProps = useAppStore((s) => s.worktreeCardProperties)
- // PR cache is needed for PR-status grouping, smart sorting, and when the
- // PR card property is visible.
+ // PR cache is needed for PR-status grouping and when the PR card property
+ // is visible.
const prCache = useAppStore((s) =>
- groupBy === 'pr-status' || sortBy === 'smart' || cardProps.includes('pr') ? s.prCache : null
+ groupBy === 'pr-status' || cardProps.includes('pr') ? s.prCache : null
)
const sortEpoch = useAppStore((s) => s.sortEpoch)
@@ -655,26 +657,40 @@ const WorktreeList = React.memo(function WorktreeList() {
// Why useMemo instead of useEffect: the sort order must be computed
// synchronously *before* the worktrees memo reads it, otherwise the
// first render (and epoch bumps) would use stale/empty data from the ref.
+ // Why a ref alongside the memo: telemetry effects need access to the most
+ // recently computed attention map without forcing every render to read it
+ // from store state again. The ref captures whatever the memo last produced
+ // for the smart branch.
+ const lastAttentionByWorktreeRef = useRef