Improve Claude status indicators and unread notifications#1325
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| const isBusy = useTaskBusy(ws.id); | ||
| const taskStatus = useTaskStatus(ws.id); | ||
| const taskUnread = useTaskUnread(ws.id); | ||
| const displayStatus = taskStatus === 'unknown' && isBusy ? 'working' : taskStatus; |
There was a problem hiding this comment.
Dropped task.status === 'running' database fallback indicator
Medium Severity
The old code checked (isRunning || ws.status === 'running') to show a spinner, using the database status field as a secondary fallback. The new displayStatus computation only falls back to isBusy (PTY-based detection) when taskStatus === 'unknown', completely dropping the ws.status === 'running' / task.status === 'running' database check. During app startup or before PTY activity classification kicks in, tasks that are actually running (per the database) won't show any status indicator.
Additional Locations (1)
| window.removeEventListener(CONVERSATIONS_CHANGED_EVENT, onChanged); | ||
| for (const off of chatUnsubs.values()) off?.(); | ||
| }; | ||
| }, [taskId, reloadChats]); |
There was a problem hiding this comment.
Substantial duplicated logic across status and unread hooks
Low Severity
useTaskStatus and useTaskUnread contain nearly identical scaffolding: the reloadChats callback, CONVERSATIONS_CHANGED_EVENT constant, syncChatIds/load/onChanged pattern, and conversation-change event wiring are all duplicated. Each independently fetches conversations from rpc.db.getConversations, manages its own subscription maps, and handles cleanup. A shared helper or combined hook would reduce the maintenance surface and risk of the two diverging.
Additional Locations (1)
Greptile SummaryThis PR introduces a Claude-backed semantic status tracking system on top of the existing PTY-busy heuristic, replacing bare Key observations:
The core state-machine logic is sound and well-tested, with no correctness-breaking bugs identified. Confidence Score: 4/5
Last reviewed commit: 69cdff8 |
| export function mapUserInputToStatus(providerId: string): AgentStatusKind | null { | ||
| if (providerId === 'claude') return 'working'; | ||
| return null; | ||
| } |
There was a problem hiding this comment.
mapUserInputToStatus returns an AgentStatusKind value that is never used. In AgentStatusStore.markUserInputSubmitted, the result is assigned to nextKind but only serves as a capability check (if (!nextKind) return); the actual 'working' string is discarded. This makes the function signature misleading — it appears to compute a status that will be applied, but it doesn't.
Consider renaming to reflect its actual role as a capability check:
| export function mapUserInputToStatus(providerId: string): AgentStatusKind | null { | |
| if (providerId === 'claude') return 'working'; | |
| return null; | |
| } | |
| export function supportsUserInputTracking(providerId: string): boolean { | |
| return providerId === 'claude'; | |
| } |
Then update markUserInputSubmitted to call supportsUserInputTracking directly.
| import { rpc } from '../lib/rpc'; | ||
|
|
||
| const EMPTY_STATUS = 'unknown' as AgentStatusKind; | ||
| const CONVERSATIONS_CHANGED_EVENT = 'emdash:conversations-changed'; |
There was a problem hiding this comment.
The string constant 'emdash:conversations-changed' is independently defined here and also in useTaskUnread.ts (line 5). Duplicate constants like this are a maintenance risk — a rename or typo in one file silently breaks the other without a compile-time error.
Extract this constant to a shared location (e.g., src/renderer/lib/events.ts or src/shared/events.ts) and import it in both hooks.
| const CONVERSATIONS_CHANGED_EVENT = 'emdash:conversations-changed'; | |
| import { CONVERSATIONS_CHANGED_EVENT } from '../lib/events'; |
|
Nice! |


Summary
Testing
Note
Medium Risk
Adds a new cross-app status/unread store wired into terminal input/output and agent events, which can affect activity indicators and notification behavior across tasks. Risk is mostly in state synchronization (active view/unread clearing) and PTY lifecycle handling, not data/security.
Overview
Adds semantic agent status + unread tracking. Introduces
agentStatusStorewith new sharedAgentStatusKindtypes and adapters to map Claude agent events (and confirmed PTY activity after user submit) into statuses, plus unread state when non-active conversations reachwaiting/complete/error.Updates UI indicators across the app. Replaces legacy busy spinners in
ChatInterfacetabs,TaskItem, andProjectMainViewrows with a unifiedTaskStatusIndicator(spinner forworking, blue dot for unread terminal states), withuseTaskBusyretained as a fallback when semantic status isunknown.Wires status lifecycle into runtime events.
Workspaceforwards agent events to the new store;TerminalSessionManagerandusePendingInjectionmark submits and feed PTY output into the store;ChatInterfacetracks the active conversation to set the visible status id and clear unread when viewing a tab. Includes new hooks (useConversationStatus,useTaskStatus,useTaskUnread,useStatusUnread) and tests for status/unread behavior.Written by Cursor Bugbot for commit 69cdff8. This will update automatically on new commits. Configure here.